std::forward_list是内存更省但操作受限的单向链表,每个节点只存后继指针,无size()、back()、rbegin(),插入删除均需基于前驱节点调用insert_after/erase_after。

std::forward_list 是单向链表,不是双向的
它只存下一个节点的指针,不像 std::list 那样每个节点都得存「前驱 + 后继」两个指针。内存省在哪?就省在这一个指针上——每个节点少 8 字节(64 位系统下)。对大量小对象(比如 int、char*)来说,这个比例很可观。
实操建议:
- 如果你确定只需要从头到尾遍历,且从不反向访问,
std::forward_list比std::list更轻量 - 它不提供
size()成员函数——因为维护长度要额外开销;调用std::distance(begin(), end())是 O(n),别误以为是 O(1) - 没有
back()或rbegin();末尾插入必须遍历到底,除非你缓存了尾迭代器(但标准库不帮你干这事)
insert_after 和 erase_after 是核心操作接口
std::forward_list 的所有修改操作都围绕「已知某节点之后」展开,比如 insert_after、erase_after、splice_after。它没有 insert(pos, val) 这种基于任意位置的插入——因为找不到「pos 的前一个节点」,就无法完成链表拼接。
常见错误现象:
立即学习“C++免费学习笔记(深入)”;
- 想在第 3 个元素前插入?不行,只能先找到第 2 个节点,再对它调用
insert_after - 写
lst.insert(lst.begin(), x)编译失败:没有重载接受iterator的insert,只有insert_after - 误用
erase(it):必须用erase_after(prev_it),传入的是前一个位置
示例:
std::forward_list<int> lst = {1, 2, 4};
auto it = lst.before_begin(); // 必须从 before_begin 开始
std::advance(it, 2); // 移动到指向 2 的节点之前 → 即 it 现在指向 2 的前驱(也就是 1)
lst.insert_after(it, 3); // 在 2 前插入?不对,在 it 之后插入 → 插入到 2 和 4 之间
// 结果:{1, 2, 3, 4}
和 vector/list 的性能取舍要看访问模式
它省内存,但换来了更弱的随机访问能力和更受限的操作语义。不是“比 list 好”,而是“在特定场景下更合适”。
使用场景判断:
- 高频头插、头删,且几乎不查长度、不反向遍历 →
std::forward_list合理 - 需要频繁在中间按索引插入/删除(比如第 i 个位置),哪怕数据量不大 → 别用它,
std::vector可能更快 - 需要稳定迭代器(插入不使其他迭代器失效),又需要双向遍历 → 回头用
std::list,别硬扛 - 编译器优化可能让
std::forward_list的遍历比预期慢:缺乏空间局部性,CPU cache 友好度差于vector
容易被忽略的细节:initializer_list 构造和 splice_after
它支持 {a,b,c} 初始化,但不支持用 = 赋值初始化(比如 auto l = {1,2,3} 推导为 std::initializer_list,不是 forward_list)。
splice_after 是唯一能高效移动节点的操作,但它要求两个 list 类型完全一致(包括分配器),且目标位置必须合法:
- 不能
splice_after(end(), other)——end()不是一个有效节点,不能作为「after」的目标 - 正确写法是
splice_after(before_begin(), other)或splice_after(some_it, other) - 移动后
other变为空,且不重新分配内存,纯粹指针改写
这个操作没有拷贝、没有构造,是真正零开销转移——但前提是你理解「after」到底 after 谁。










