std::forward_list 的 size() 在 C++17 前是 O(n),因老标准未强制要求缓存大小,libstdc++ 等实现为省 8 字节和增删开销而遍历计数,符合“零开销抽象”原则。

std::forward_list 不是“轻量版 std::list”,它是为特定硬件和算法约束设计的单向链表原语;用错场景时,它比 std::list 还难维护、比 std::vector 还慢。
为什么 std::forward_list 的 size() 在老项目里一调就变 O(n)?
因为 C++17 才强制要求 size() 是 O(1),而你用的 libstdc++(GCC 默认)或某些嵌入式 STL 实现,在 C++14 模式下压根没存计数器——每次调用都得从头遍历到尾。这不是 bug,是设计选择:省那 8 字节(64 位指针)和每次插入/删除时的 +1/-1 开销,符合“零开销抽象”原则。
- 别在循环条件里写
for (size_t i = 0; i ,这会把 O(n) 遍历变成 O(n²) - 判断是否为空,永远用
fl.empty()—— 它是 O(1),且所有标准库实现都保证 - 真需要长度且频繁访问?说明
std::forward_list不适合你,换std::list或带计数器的自定义 wrapper
insert_after 和 erase_after 为什么不能传 end()?
end() 不是指向“最后一个节点”,而是指向“逻辑末尾之后”的无效位置;insert_after() 和 erase_after() 都要求参数迭代器必须指向一个**真实存在的节点**(哪怕只是头节点前的 before_begin()),因为它们的操作语义是“在它后面做某事”。传 end() 就像对空气说“请在我右边插个数”——未定义行为。
- 想在开头插入?必须用
fl.insert_after(fl.before_begin(), val) - 想删第一个元素?用
fl.erase_after(fl.before_begin()) - 想删第 n 个(n ≥ 1)?先
auto it = fl.begin(); std::advance(it, n-1);,再fl.erase_after(it) - 想在末尾插入?得先遍历到倒数第二个节点,再
insert_after();没有push_back(),也不提供尾迭代器支持
splice_after() 是唯一真正 O(1) 的拼接操作
std::forward_list::splice_after() 不拷贝元素、不调用构造/析构、不分配内存,只改几个 next 指针。这是它在 LRU 缓存、事件队列迁移等高频重组场景中不可替代的核心价值。但注意:它不像 std::list::splice() 那样灵活,只能拼“从某点开始到结尾”或“某段范围”,且 src 和 dst 必须是同一类型、不能是自身。
立即学习“C++免费学习笔记(深入)”;
-
dst.splice_after(pos, src):把整个src拼到dst的pos后,src变空 -
dst.splice_after(pos, src, it):把src中it指向的**单个节点**移到dst的pos后 -
dst.splice_after(pos, src, first, last):把src中[first, last)范围的节点拼过去 - 误用
splice_after(pos, dst, ...)(自拼接)会导致未定义行为,编译器不会报错
什么时候该选 std::forward_list?不是“想要链表”,而是满足三个硬条件
选 std::forward_list 不是因为它“看起来像链表”,而是因为你同时满足:只从前向后遍历、节点数量大且值类型极小(如 int、char*、short)、插入/删除集中在头部或已知前驱位置。缺一不可。
- 典型合适场景:哈希桶冲突链(每个桶只线性扫描)、LRU 缓存链表(配合哈希表存前驱迭代器)、解析器临时 token 链(生命周期短、节点少)
- 典型误用场景:需要
size()做分页、要std::sort(fl.begin(), fl.end())(得用fl.sort()成员函数)、想用 map 存当前节点迭代器(它不支持随机访问,map 存的必须是前驱) - 内存节省只有在节点数 ≥ 10⁵ 且值类型 ≤ 8 字节时才显著;存
std::string或结构体?省下的那 8 字节连 cache line 对齐的零头都不够
最常被忽略的一点:它的迭代器不是 BidirectionalIterator,不支持 --it 或 std::prev();任何依赖双向移动的泛型算法(比如 std::find_end、std::search)都不能直接用在 std::forward_list 上。



















