std::forward_list不支持operator[]和size()因无随机访问且c++11中size()为o(n),仅c++17起要求o(1);迭代器仅为forward_iterator,所有操作需基于before_begin()和erase_after()等单向语义。

std::forward_list 为什么不能用 operator[] 或 size()?
因为 std::forward_list 是为最小内存开销和最大插入/删除效率设计的,它不存储节点数,也不支持随机访问。调用 size() 在 C++11 中是 O(n),C++17 起才强制要求为 O(1),但很多旧编译器(如 GCC 7 及更早)仍按 O(n) 实现——别依赖它做频繁计数。
常见错误现象:mylist[5] 编译失败;mylist.size() 在循环里反复调用导致性能骤降。
- 需要索引访问时,改用
std::list或std::vector - 真要遍历到第 N 个元素,用
std::next(mylist.begin(), n)(需确保 n std::distance 结果) - 统计长度前先确认编译器标准(
__cplusplus >= 201703L)和 STL 版本
插入和删除操作必须用迭代器,且仅支持单向移动
std::forward_list 的迭代器是 forward_iterator,只能 ++,不能 -- 或 +=。所有修改操作(insert_after、erase_after、splice_after)都以“在某迭代器之后”为语义,没有“在开头插入”这种直觉操作——得用 before_begin()。
典型误用:mylist.insert(it, value) 编译报错,因为没这个重载;mylist.erase(--it) 根本不可行。
- 在头部插入:用
mylist.push_front(x)(等价于mylist.insert_after(mylist.before_begin(), x)) - 在 pos 后插入:必须用
mylist.insert_after(pos, value),其中pos是合法迭代器或before_begin() - 删除某个值:不能直接 erase 值,得先 find,再
erase_after(prev_it)—— 所以通常要维护前驱迭代器
如何安全地遍历并条件删除元素?
不能边遍历边用普通 for + erase(),因为 forward_list::erase_after() 会失效其参数之后的所有迭代器。正确做法是用 before_begin() 驱动,手动推进前驱位置。
示例:删除所有偶数值
auto prev = flist.before_begin();
auto curr = flist.begin();
while (curr != flist.end()) {
if (*curr % 2 == 0) {
curr = flist.erase_after(prev); // erase_after 返回被删节点后的迭代器
} else {
prev = curr;
++curr;
}
}
关键点:
-
erase_after(prev)删除的是prev指向节点的下一个节点,返回新“下一个”迭代器 - 不要对
curr做++后再 erase,否则跳过元素或解引用 end() - 如果用
remove_if,它内部已处理好这个逻辑,可直接写flist.remove_if([](int x){ return x % 2 == 0; });
与 std::list 和 std::vector 的实际选型边界在哪?
别只看“链表”就选 forward_list。它真正优势只有两个场景:内存极度敏感(比如嵌入式中每个字节都要抠),或高频在头部/中间做插入/删除且从不查长度、不反向遍历、不随机访问。
容易被忽略的代价:
- 每次
push_front都要 new 一个节点,而vector在容量够时零分配 - 缓存不友好:节点分散堆上,遍历比
vector慢 3–5 倍以上 - 没有
merge()成员函数(list有),合并两个有序链表得手写或用std::inplace_merge配合临时 vector
绝大多数业务代码里,std::vector 配合 erase(remove_if(...), end()) 比折腾 forward_list 更快更安全。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











