std::erase_if 比手写 remove-erase 更安全,因为它自动调用容器成员 erase 避免迭代器失效和范围计算错误,防止因漏用返回迭代器、误用 end() 或谓词中修改容器导致的未定义行为。

std::erase_if 为什么比手写 remove-erase 更安全
因为 std::erase_if 是 C++20 引入的容器感知删除函数,它内部自动调用 erase 成员函数(而非泛型 std::erase),避免了迭代器失效和手动计算 erase 范围的错误。手写 remove_if + erase 容易漏掉返回的迭代器、误用 end()、或在 lambda 中意外修改 vector 导致未定义行为。
常见错误现象:vector iterator not incrementable(MSVC)、segmentation fault(GCC/Clang),多发生在循环中边遍历边 erase,或 remove_if 后没用返回值做 erase。
- 必须用 C++20 或更高标准编译(
-std=c++20),否则编译失败 -
std::erase_if是非成员函数,头文件只需<algorithm></algorithm>,不依赖<vector></vector>显式包含(但 vector 本身当然要包含) - 对
std::vector,它等价于调用v.erase(std::remove_if(v.begin(), v.end(), pred), v.end()),但更简洁、不易出错
删除偶数、空字符串、过期对象:典型谓词写法
谓词可以是 lambda、函数指针或函数对象,关键是返回 bool 且不修改元素(除非你明确需要副作用)。lambda 最常用,捕获方式要谨慎。
示例:删除所有偶数
std::vector<int> v = {1, 2, 3, 4, 5, 6};
std::erase_if(v, [](int x) { return x % 2 == 0; }); // v 变成 {1, 3, 5}
</int>
示例:删除空字符串(注意 std::string 的 empty() 成员函数)
std::vector<:string> vs = {"", "a", "", "bc", ""};
std::erase_if(vs, [](const std::string& s) { return s.empty(); }); // vs 变成 {"a", "bc"}
</:string>
- 捕获局部变量时用
[&]或[=]都可以,但避免[this]在 lambda 中隐式调用成员函数导致生命周期问题 - 谓词参数建议用
const T&(尤其对大对象),避免不必要的拷贝 - 不要在谓词里调用
vector::erase或修改容器本身,会破坏std::erase_if的内部逻辑
兼容旧标准(C++17 及以前)的等效写法
如果项目不能升级到 C++20,必须手写 remove-erase 惯用法。注意:不能直接用 std::remove_if 返回的迭代器传给 std::erase(后者是 C++20 才有的泛型擦除函数),而必须调用 vector::erase 成员函数。
std::vector<int> v = {1, 2, 3, 4, 5, 6};
auto new_end = std::remove_if(v.begin(), v.end(), [](int x) { return x % 2 == 0; });
v.erase(new_end, v.end()); // ✅ 正确:调用成员函数
// v.erase(std::remove_if(...)); ❌ 编译失败(C++17 不支持该重载)
</int>
-
std::remove_if不真正删除元素,只是把满足条件的元素“移走”,返回新逻辑结尾的迭代器 - 必须用
v.erase(it, v.end()),不能省略第二个参数(v.end()) - 顺序不能颠倒:先
remove_if,再erase;反过来会导致未定义行为
性能与异常安全性注意事项
std::erase_if 对 std::vector 的时间复杂度是 O(n),空间复杂度 O(1),和手写 remove-erase 完全一致。但它在异常安全性上略优:如果谓词抛异常,std::erase_if 保证容器处于有效状态(已移动元素不会被重复析构)。
- 若谓词中抛异常,已执行的元素移动仍有效,剩余元素位置不变 —— 这点和手写版本一样
- 但
std::erase_if内部实现更可能做小优化(如避免多次调用swap),实际差异微乎其微 - 对
std::list或std::forward_list,std::erase_if会调用对应容器的remove_if成员函数,效率更高;但 vector 没有成员版erase_if,所以仍是基于 move + erase
最常被忽略的一点:如果你在循环中反复调用 std::erase_if 处理同一个 vector,要注意每次调用都会触发内存移动 —— 这不是 bug,但高频调用可能成为性能瓶颈,此时应考虑预筛选或改用其他容器(如 std::deque 或索引向量)。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











