std::erase_if是C++20引入的非成员函数,封装了remove_if+erase两步,对vector、list、map等标准容器支持一行删除;不适用于自定义容器或迭代器范围,C++17及以前需手动组合erase和remove_if。

erase_if 本身就要配合容器的 erase 成员函数一起用
标准库里的 std::erase_if(C++20 引入)不是容器成员函数,而是针对特定容器的非成员函数,它内部调用容器的 erase 成员完成真实删除。所以“一行代码”是成立的,但前提是容器类型被支持(std::vector、std::deque、std::list、std::forward_list、std::set 等),且编译器启用了 C++20 或更高标准。
常见错误是误以为 std::erase_if 是泛型算法(像 std::remove_if 那样),结果对 std::vector 写成:
std::remove_if(v.begin(), v.end(), pred); // ❌ 不删元素,只重排
这不会真正删除,必须配 erase 才行 —— 而 std::erase_if 就是把这两步封装好了。
怎么写才真是一行?看容器类型
对 std::vector、std::deque、std::list 等,直接传容器和谓词:
立即学习“C++免费学习笔记(深入)”;
-
std::erase_if(v, [](int x) { return x % 2 == 0; });—— 删除所有偶数 -
std::erase_if(mymap, [](const auto& p) { return p.second.empty(); });—— 删除 value 为空的 map 元素 -
std::erase_if(s, [](char c) { return std::isspace(c); });—— 从std::string删空格(C++20 支持std::string特化)
注意:std::erase_if 对 std::unordered_map 也有效,但对自定义容器无效;也不接受迭代器范围(比如不能写 std::erase_if(v.begin()+1, v.end(), pred))。
没 C++20 怎么办?自己模拟一行(但本质不是一行)
如果卡在 C++17 或更早,没有 std::erase_if,有人会用 lambda 包一层假装“一行”:
[](auto& c, auto pred) { c.erase(std::remove_if(c.begin(), c.end(), pred), c.end()); }(v, [](int x){return x<0;});
这语法合法,但可读性差、容易出错,而且 std::remove_if + erase 对 std::list 是低效的(list::remove_if 成员函数才是 O(n))。更稳妥的做法是按容器选原生接口:
-
v.erase(std::remove_if(v.begin(), v.end(), pred), v.end());—— vector/deque -
lst.remove_if(pred);—— list/forward_list(直接成员函数,最高效) -
for (auto it = m.begin(); it != m.end(); ) if (pred(*it)) it = m.erase(it); else ++it;—— map/set,需手动迭代
容易被忽略的性能和语义坑
std::erase_if 对不同容器的实现策略不同,不是统一“先 remove_if 再 erase”:
- 对
std::vector:等价于erase(remove_if(...)),稳定,但移动开销大 - 对
std::list:调用list::remove_if,不移动节点,只改指针 - 对
std::map:逐个检查并调用erase(iterator),无重排,但平均复杂度 O(n log n)
另外,谓词里不要修改容器本身(比如在 lambda 里 push_back),行为未定义;也不要依赖元素地址(vector 删除后内存可能重分配)。
真正“一行删除”的简洁性只在 C++20+ 且容器受支持时成立;否则所谓一行只是语法糖,掩盖了底层差异和潜在低效。


















