应优先使用C++20引入的std::erase_if,它封装了“逻辑删除+物理收缩”过程,安全简洁;手动for循环+erase会因迭代器失效导致越界或漏删,而旧标准需用std::remove_if+erase组合实现等效功能。

直接用 std::erase_if,C++20 起原生支持,简洁安全,不用手写循环+erase+remove_if组合。
为什么不能直接用 for 循环 + erase
边遍历边删会破坏迭代器有效性,导致越界或跳过元素。常见错误现象是:只删掉部分匹配项,或者程序崩溃(vector::erase 使后续迭代器失效)。
典型翻车写法:
for (auto it = v.begin(); it != v.end(); ++it) {
if (*it % 2 == 0) v.erase(it); // ❌ it 失效,下一次 ++it 行为未定义
}
正确思路是「逻辑删除 + 物理收缩」,而 std::erase_if 就是这个过程的封装。
std::erase_if 的基本用法和参数要求
它是个非成员函数,定义在 (也适用于 deque、list 等),接受容器引用和一个一元谓词(返回 bool)。
使用要点:
- 必须包含
(C++20);老标准(C++17 及以前)没有该函数,需手动用std::remove_if+erase - 谓词可以是 lambda、函数指针或函数对象,捕获变量需注意生命周期(尤其在 lambda 中引用局部容器元素时)
- 它会移动所有保留元素,时间复杂度 O(n),空间 O(1)
- 返回被删元素个数(C++20 起),可用于判断是否真有删除发生
示例:
std::vector<int> v = {1, 2, 3, 4, 5, 6};
auto n = std::erase_if(v, [](int x) { return x % 2 == 0; });
// v 变成 {1, 3, 5},n == 3</int>
替代方案:C++17 及以前怎么写等效逻辑
如果项目还卡在 C++17 或更早,得自己组合 std::remove_if 和 erase:
-
std::remove_if不真正删元素,而是把满足条件的“往后挪”,返回新逻辑尾迭代器 - 再用
v.erase(返回迭代器, v.end())做物理截断 - 顺序不能反,否则删的是错的区间
示例:
std::vector<:string> words = {"cat", "dog", "bird", "fish"};
words.erase(
std::remove_if(words.begin(), words.end(),
[](const std::string& s) { return s.length() > 3; }),
words.end()
);
// words 变成 {"cat", "dog"}</:string>
注意:remove_if 对 vector 是稳定操作(保持剩余元素相对顺序),但性能上比 erase_if 多一次移动——不过对多数场景无感。
容易忽略的坑:自定义类型、移动语义与异常安全
std::erase_if 内部调用 erase 和移动构造/赋值,所以要注意:
- 若 vector 元素类型不可移动(如禁用了移动构造函数),且不满足“可复制”条件,编译失败
- 移动过程中若抛异常(比如移动构造函数 throw),整个操作可能处于中间状态(已移但未删完),但标准保证容器仍有效(不会泄露、不会破坏结构)
- lambda 捕获局部变量时,别用
[&]引用一个即将析构的对象(比如在函数栈上临时构造的std::regex)
例如这个危险写法:
void bad_example() {
std::regex r("a+");
std::vector<:string> v = {"a", "aa", "b"};
std::erase_if(v, [&](const std::string& s) { return std::regex_match(s, r); }); // ❌ r 在 lambda 调用时可能已析构
}</:string>
实际项目中,优先用 std::erase_if;如果编译器不支持 C++20,就老实用 remove_if+erase 组合。最麻烦的不是写法,而是忘记检查编译标准版本,或者在 lambda 里误捕获了短生命周期对象。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











