c++oding="utf-8" ?>
fl.empty()是唯一可移植的空判断方式,因其保证o(1)且标准强制定义;size()在主流libstdc++中为o(n)遍历,部分裁剪stl甚至不提供,误用会导致性能雪崩或崩溃。

别用 size() 判空,也别传 end() 给 erase_after()——这两类操作在 GCC 默认 libstdc++、嵌入式裁剪 STL 或 C++14 项目里,轻则性能雪崩,重则静默崩溃或未定义行为。
为什么 fl.empty() 是唯一可移植的空判断方式
size() 在当前主流 libstdc++(GCC 12/13/14 默认)中仍是 std::distance(fl.begin(), fl.end()),即全链表遍历;部分裁剪版 STL 根本不提供该函数,调用直接编译失败;更糟的是,某些旧实现返回未初始化值,if (fl.size() == 0) 可能恒为 false 或触发 SIGSEGV。
- 永远用
fl.empty()替代fl.size() == 0或!fl.size() - 禁止依赖
__size()等非标扩展——MSVC 可能有,libstdc++ 没 - 若封装类需高频查长度(如 LRU 缓存计数),手动维护
size_t m_size,并在每次push_front()、insert_after()、erase_after()后同步增减
insert_after() 和 erase_after() 的参数永远指“前驱节点”
这两个函数的操作对象不是你传入的迭代器本身,而是它「后面那个节点」。这是单向链表没有 prev 指针的硬约束,不是 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 - 绝对禁止传
fl.end()给任何函数——未定义行为,Clang/GCC 常静默崩溃
为什么 splice_after() 是唯一不可替代的 O(1) 拼接操作
splice_after() 是 std::forward_list 中唯一真正不调用构造/析构、不分配内存、不拷贝数据、只改 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))是未定义行为
真正省内存只在「节点数量极大 + 值类型极小」时才显著(64 位下每个节点省 8 字节),但代价是放弃随机访问、反向遍历、尾部操作和可靠长度——用之前先确认场景是否匹配:纯前向流式处理、头部/局部高频增删、内存极度敏感。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











