c++oding="utf-8" ?>
std::forward_list 是专为单向遍历、频繁头插/头删及极致内存节省设计的容器;它不提供 o(1) size() 以遵守零开销原则,应使用 empty() 判断空状态,需长度时仅调用一次 std::distance 并缓存。

std::forward_list 不是“更轻量的 list”,它是为特定约束场景设计的专用结构:当你确定只从前向后遍历、频繁头插/头删、且每字节内存都关键时,它才值得选。
为什么 std::forward_list 没有 size() 成员函数
不是忘了加,而是标准明确禁止在构造/修改时维护计数器——那会破坏“零开销”原则。C++17 要求 size() 是 O(1),但很多嵌入式或裁剪版 STL(如旧版 libstdc++)仍不提供,或返回 O(n) 结果。
- 永远用
empty()判断是否为空,它是 O(1) 且可移植 - 需要长度?只调用一次
std::distance(fl.begin(), fl.end()),并缓存结果 - 千万别写
for (size_t i = 0; i —— 这会让循环从 O(n) 变成 O(n²) - 若长度查询高频,说明
std::forward_list本身就不适合你,该换std::list或加个手动size_t count
insert_after 和 erase_after 是唯一合法的中间操作入口
单向链表没有前驱指针,所以无法从某个迭代器反推上一个节点。所有“在某位置之后插入”或“删除某位置之后的节点”都必须显式提供前驱迭代器。
-
fl.insert_after(it, val)总是在it所指节点之后插入,it必须有效(不能是fl.end()) - 想插到开头?用
fl.insert_after(fl.before_begin(), val),等价于push_front() - 想删第一个元素?用
fl.erase_after(fl.before_begin()),等价于pop_front() - 想删第 n 个(n ≥ 1)?先
auto prev = std::next(fl.begin(), n - 1),再fl.erase_after(prev) - 传
fl.end()给erase_after()是未定义行为,编译可能过,运行必崩
splice_after() 是它唯一不可替代的性能优势
其他容器的拼接要拷贝或移动元素,std::forward_list::splice_after() 是纯指针操作:改几个 next 字段,不调构造、不调析构、不分配内存。
-
dst.splice_after(pos, src):把整个src接到dst的pos后,src清空 -
dst.splice_after(pos, src, it):把src中it指向的单个节点移到dst的pos后 -
src和dst必须是同一类型,且不能是同一个对象(自拼接未定义) - 它不支持像
std::list::splice()那样按范围切一段拼过去——只能从it开始拼到末尾,或指定一对迭代器
什么时候该用 std::forward_list,什么时候不该
省下的每个字节都来自放弃功能:没 back()、没 operator[]、没反向迭代器、没尾指针。这些不是缺陷,是契约。
- 适合:
std::forward_list<int></int>存百万级小整数;LRU 缓存中配合哈希表快速移除/前置节点;事件队列只从头部消费 - 不适合:
std::forward_list<:string></:string>(字符串本身堆分配,指针差异可忽略);需要push_back()或随机访问的场景;任何依赖size()做逻辑分支的通用算法 - 误用信号:你开始自己维护尾迭代器、或反复写
std::prev()并发现编译失败——说明你其实想要std::list - 真正关键点:它的价值不在“能做什么”,而在“不做哪些事”——少存一个指针、不维护计数、不支持反向遍历,这些“不作为”才是它省空间和保性能的根本
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











