std::remove_if仅重排元素并返回新逻辑尾迭代器,必须配合erase才能真正删除;需注意lambda捕获生命周期、容器类型适配及谓词返回true表示删除。

remove_if 本身不删除元素,只是移动
std::remove_if 不是真的从容器里删掉东西,它只把“该删”的元素挪到末尾,返回一个指向新逻辑结尾的迭代器。真正删掉得靠容器自己的 erase 配合——这叫“erase–remove惯用法”。漏掉 erase 这一步,容器大小不变,数据还留在那里,只是顺序乱了。
常见错误现象:vec.size() 没变,打印出来发现“删了但还在”;或者后续遍历时访问到被移走的旧值。
- 必须和
erase成对使用:vec.erase(std::remove_if(vec.begin(), vec.end(), pred), vec.end()) - 只对支持随机访问或双向迭代器的容器有效(
vector、list、deque可以,forward_list得用remove_if成员函数) - 对
list,直接用lst.remove_if(pred)更高效,不用配erase
lambda 捕获变量时注意生命周期
在 lambda 里捕获局部变量(比如 [x] 或 [&x])没问题,但若 lambda 存活时间超过捕获变量的作用域,就会出问题。典型场景是把 lambda 存进容器、传给异步任务,或在循环中反复生成并保存。
使用场景:按某个外部阈值过滤,比如 int threshold = 42;,然后删掉所有小于它的元素。
立即学习“C++免费学习笔记(深入)”;
- 值捕获
[threshold]安全,复制一份,lambda 自己管生命周期 - 引用捕获
[&threshold]危险,如果threshold在remove_if调用完就析构,lambda 执行时就是悬垂引用 - 捕获
this要小心:类成员函数里写[this]没问题,但确保 lambda 不逃逸出对象生命周期
vector 和 list 的性能差异很大
vector::erase 删除中间元素要搬动后面所有元素,而 remove_if + erase 是单次搬运,复杂度仍是 O(n);但 list::remove_if 是链表指针操作,没有数据搬动,平均更快,尤其删得多时。
错误预判:以为 list 用 std::remove_if + erase 也高效——其实它会把节点拷贝来拷贝去(因为 std::remove_if 基于赋值),严重拖慢。
- 对
vector:用erase(remove_if(...))是标准写法 - 对
list:优先用成员函数lst.remove_if([](auto& x) { return ...; }) - 对
forward_list:只能用成员函数flst.remove_if(...),标准算法不支持前向迭代器的 remove_if
注意 predicate 返回 true 表示“要删”,不是“保留”
这是最常翻车的地方。很多人直觉以为 remove_if 里写“条件成立就留下”,结果全删光了。记住口诀:true = gone。
示例:想删掉所有偶数,lambda 应该写 [](int x) { return x % 2 == 0; },而不是 != 0。
- predicate 参数类型要匹配容器元素类型(
const T&最安全,避免意外拷贝) - 如果容器是
vector,别写[](string s),用[](const string& s) - 返回类型自动推导,但别在 lambda 里写
return false;然后下面又return true;却没加else——编译可能过,但逻辑错得隐蔽
erase 必须跟上、捕获方式得盯住生命周期、容器类型决定你该调哪个 remove_if。漏掉任何一环,程序都可能跑得对但结果错,或者跑着跑着崩。









