std::erase_if是c++20引入的最简洁删除方案,自动完成逻辑移除与容器收缩;低于c++20须用std::remove_if+erase()配对,否则仅挪动元素而未真正删除。

直接删空元素不用手写循环,std::erase_if 是最干净的解法,但要注意 C++20 才有;低于 C++20 必须配对用 std::remove_if + erase(),漏掉后者就只是“挪位置”,实际没删。
什么时候该用 std::erase_if?
只在 C++20 及以上可用,底层自动完成“逻辑移除 + 容器收缩”两步,语义清晰、代码少、不易出错。
- 适用容器:仅限支持
erase(iterator, iterator)的序列容器(如std::vector、std::deque、std::list) - 判断逻辑必须是纯函数:不能修改元素本身,否则行为未定义
- 删除后迭代器全部失效(这是 vector 的正常表现,不是 bug)
示例(C++20):
std::vector<:string> v = {"a", "", "b", "", "c"};
std::erase_if(v, [](const std::string& s) { return s.empty(); });
// v 现在是 {"a", "b", "c"}
</:string>
为什么 std::remove_if 单独用等于白干?
std::remove_if 不是真正删除,它只是把要保留的元素往前拷贝,返回一个“新逻辑尾部”迭代器;原容器 size 不变,后面是未定义值(可能还是旧数据)。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 常见错误:只写
std::remove_if(v.begin(), v.end(), ...),没接v.erase(...) - 更隐蔽的坑:对
vector<bool></bool>使用remove_if会编译失败(因为它是特化位容器,迭代器不满足要求) - 性能上,
remove_if+erase和erase_if在 vector 上复杂度一致(都是 O(n)),但前者多一次函数调用开销
正确写法(C++11 起通用):
std::vector<:string> v = {"a", "", "b", "", "c"};
v.erase(
std::remove_if(v.begin(), v.end(), [](const std::string& s) { return s.empty(); }),
v.end()
);
</:string>
空元素判断别只盯 empty(),得看类型
“空”的定义取决于元素类型。对 std::string 是 empty(),对 std::shared_ptr<t></t> 是 !ptr,对原始指针是 ptr == nullptr,对数值类型(如 int)则需明确“空”是否指 0——这属于业务逻辑,标准库不管。
- 字符串:
s.empty()或s == ""(前者更快) - 智能指针:
!p比p == nullptr更推荐(可读性好,且兼容自定义删除器) - 原始指针:
p == nullptr,避免用!p(虽等价但易被误读为布尔逻辑) - 自定义类:必须已定义合适的判空逻辑(如
obj.is_null()或!obj重载)
删完记得检查迭代器有效性
无论用哪种方法,只要删了元素,所有指向被删位置及之后的 vector 迭代器、引用、指针全部失效。尤其注意循环中边遍历边删的写法——那是未定义行为。
- 反例:
for (auto it = v.begin(); it != v.end(); ++it) { if (it->empty()) v.erase(it); }——erase后it失效,再++it就崩 - 正解:要么用
erase_if/remove_if一次性处理;要么手动控制迭代器,如it = v.erase(it)(此时erase返回下一个有效位置) - 如果必须边查边删,优先选
while+erase返回值,别依赖for自增
真正的麻烦点不在语法,而在“删完之后谁还在用旧迭代器”——这种 bug 往往不报错,只在特定数据下偶发崩溃或读脏值。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










