c++oding="utf-8" ?>
应使用 fl.empty() 判空而非 size(),因 size() 在部分 STL 实现中为 O(n) 遍历、未定义或致崩溃;需长度时应缓存 std::distance 结果或手动维护计数器。

别用 size() 判空,也别传 end() 给 erase_after()——这两类操作在 GCC + libstdc++、嵌入式裁剪 STL 或 C++14 旧项目中,轻则性能雪崩,重则直接崩溃。
为什么 fl.size() == 0 是危险写法
size() 在当前主流 libstdc++(GCC 12/13 默认)中仍是 std::distance(fl.begin(), fl.end()) 全链表遍历,O(n);某些裁剪版 STL 根本不提供该函数,编译失败;部分旧实现返回未初始化值,if (fl.size() == 0) 可能恒为 false 或触发 SIGSEGV。
- 判空唯一可移植写法是
fl.empty(),它始终 O(1) 且 guaranteed defined - 若真需长度且只调一次:
auto len = std::distance(fl.begin(), fl.end()),结果存局部变量复用 - 若长度需高频访问(如 LRU 缓存计数器):手动维护
size_t m_size,并在每次push_front()、insert_after()、erase_after()后同步增减 - 绝对禁止在循环条件里写
for (size_t i = 0; i ——这会让 O(n) 遍历退化成 O(n²)
insert_after() 和 erase_after() 的参数永远指“前驱”
这两个函数的操作对象不是你传进去的迭代器本身,而是它「后面那个节点」。这是单向链表无法反推前驱的硬约束,不是 API 设计疏漏。传错就删错、插错,且无编译期提示。
- 头插安全写法:
fl.push_front(x)或fl.insert_after(fl.before_begin(), x);绝不能用fl.insert_after(fl.begin(), x) - 删首节点必须用
fl.erase_after(fl.before_begin())或fl.pop_front();传fl.begin()实际删的是第二个元素 - 删第 n 个元素(n ≥ 1):
auto prev = std::next(fl.before_begin(), n - 1),再fl.erase_after(prev)——注意是n - 1,不是n - 边遍历边删匹配节点:初始化
auto prev = fl.before_begin(),循环内用auto curr = std::next(prev)定位,匹配后执行prev = fl.erase_after(prev)(它返回被删节点之后的有效迭代器) - 绝对禁止传
fl.end()给任一函数——未定义行为,GCC/Clang 常静默崩溃
为什么 splice_after() 是唯一不可替代的核心操作
splice_after() 是 std::forward_list 中唯一真正 O(1) 的物理拼接操作:不调用构造/析构、不分配内存、不拷贝数据,只修改 next 指针。所有用 insert_after() 循环模拟拼接的行为,都会触发 n 次内存分配和拷贝,彻底毁掉它的内存与性能优势。
- 整链迁移:
dst.splice_after(pos, src),执行后src变为空容器 - 单节点迁移:
dst.splice_after(pos, src, it),将src中it所指节点移至dst的pos后,it在src中立即失效 - 范围迁移:
dst.splice_after(pos, src, first, last),仅拼接[first, last)(左闭右开) -
src和dst必须同类型且不能是同一对象;自拼接(如fl.splice_after(fl.before_begin(), fl, it))是未定义行为
最易被忽略的一点:当你把 std::forward_list 当作“轻量 std::list”用时,已经误入歧途。它只在纯前向流式处理 + 头部/局部高频增删 + 节点多 + 值类型小时才真正发挥价值;一旦需要按索引查、反向遍历或稳定长度查询,换容器比修逻辑更省时间。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











