std::remove_if 不真正删除元素,仅重排保留元素并返回新逻辑尾迭代器;需配合 erase 才能真正移除并收缩容器。

remove_if 为什么不能直接删掉元素
std::remove_if 实际上不删除任何元素,它只是把“要保留”的元素往前挪,返回一个指向新逻辑尾部的迭代器。原 vector 大小不变,后面是未定义值(或旧值)。真正删掉内存空间,得配合 erase —— 这就是经典的 erase-remove 惯用法。
常见错误是只调用 remove_if 就以为删完了,结果 size() 没变,遍历时还可能访问到残留数据。
- 必须写成
v.erase(remove_if(v.begin(), v.end(), pred), v.end()) -
pred是可调用对象,lambda 最常用,捕获方式要小心(比如引用捕获局部变量时,确保 lambda 生命周期不超出生命周期) - 如果
vector存的是指针或智能指针,remove_if只搬指针,不释放所指对象
lambda 捕获外部变量删指定值
想删掉所有等于某个变量 target 的元素?直接在 lambda 中用 [target] 值捕获最安全:
int target = 42;
vec.erase(remove_if(vec.begin(), vec.end(), [target](const int& x) { return x == target; }), vec.end());
若 target 是大对象(如 std::string),建议用 [&target] 引用捕获避免拷贝;但务必确认 target 在整个 erase-remove 过程中有效(比如别在 lambda 外提前销毁)。
立即学习“C++免费学习笔记(深入)”;
- 不要写
[=]捕获全部,容易意外拷贝不需要的变量,也模糊意图 - 如果要删满足复合条件的元素(如“大于 10 且是偶数”),lambda 主体里写清楚逻辑,别硬塞进一行
- 注意 const 正确性:参数用
const T&避免不必要的拷贝,尤其对非 POD 类型
删满足自定义类成员条件的元素
假设 vector,想删掉所有 age 的人,lambda 直接访问成员即可:
people.erase(remove_if(people.begin(), people.end(), [](const Person& p) { return p.age < 18; }), people.end());
但如果需要根据外部状态判断(比如删掉 name 在黑名单里的),就得捕获黑名单容器:
- 用
[&blacklist]引用捕获unordered_set,查起来快 - 若黑名单是临时构造的,改用
[blacklist = std::move(tmp)]转移所有权 - 别在 lambda 里修改捕获的容器(除非明确加
mutable,且知道线程安全风险)
性能和迭代器失效要注意什么
remove_if + erase 是 O(n) 时间、O(1) 额外空间,比循环中反复调用 erase(每次删都移动后面所有元素,最坏 O(n²))高效得多。
但要注意:
- 过程中所有指向被“挪走”元素的迭代器、指针、引用都会失效(即使没被删,位置也变了)
- 如果删完还要继续用某些元素的指针,得在
remove_if前保存它们的值或索引,而不是地址 -
vector场景下,> remove_if会移动unique_ptr,自动转移所有权,原位置变成空,这是安全的
最易忽略的是:lambda 里抛异常会导致 remove_if 行为未定义(标准不保证强异常安全),所以 predicate 内尽量别抛,或确保它 noexcept。










