因为erase会令后续元素前移,导致跳过下一个元素;正确做法是用erase返回的迭代器或反向遍历。

为什么不能用 for 循环边遍历边 erase
直接写 for (int i = 0; i 然后对偶数调用 <code>v.erase(v.begin() + i) 会导致跳过元素。因为 erase 后后面所有元素前移,而 i 还在递增,下标就“跨过去”了一个位置。
比如 v = {2,4,6,1,3},删掉 2(索引 0)后,4 移到索引 0,但循环下一步检查索引 1,也就是原来的 6,4 就被漏掉了。
推荐做法:用 remove_if + erase(惯用法)
std::remove_if 不真正删除,而是把要保留的元素往前挪,返回一个新逻辑尾迭代器;再用 erase 一次性删掉后面那段——这是标准、高效、安全的做法。
- 头文件必须包含:
#include <algorithm></algorithm>和#include <vector></vector> - 判断条件写成 lambda 最清晰:
[](int x) { return x % 2 == 0; } - 完整写法:
std::vector<int> v = {1,2,3,4,5,6}; v.erase(std::remove_if(v.begin(), v.end(), [](int x) { return x % 2 == 0; }), v.end()); - 注意:
remove_if返回的是“新末尾”,不是被删元素个数,别传错参数
如果需要保留原始顺序且不想改原容器
有时你不想就地修改,或想明确看到哪些被删了。这时可以新建 vector 存奇数:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
立即学习“C++免费学习笔记(深入)”;
- 用
std::copy_if更直观:std::vector<int> odd; std::copy_if(v.begin(), v.end(), std::back_inserter(odd), [](int x) { return x % 2 != 0; }); - 或者用 range-based for 手动 push:
for (int x : v) if (x % 2 != 0) odd.push_back(x); - 这种写法内存开销略大,但逻辑最直白,调试时也容易加日志
性能和类型注意事项
对 vector<int> 这种小对象,remove_if + erase 是最优解;但若元素是大对象(比如 std::string 或自定义类),要考虑移动语义是否启用:
- 确保类有移动构造函数,否则
remove_if内部大量拷贝会变慢 - 如果是
vector<unique_ptr<T>>,移除操作本身很快,因为只搬指针 - 不要对
vector<bool>用这个——它是特化模板,iterator不是真实指针,remove_if行为可能不符合预期
实际写的时候,先确认元素类型和性能敏感度,再选方案。多数情况直接上 remove_if + erase 就行,但得记着它不保证异常安全——如果 lambda 抛异常,容器处于中间状态。真有强异常保证需求,得手动循环加 swap-and-pop 之类策略。

















