误用erase-remove惯用法时std::erase未报错的问题及优化方案问询
关于erase-remove惯用法的错误行为与正确实现
一、错误代码的行为分析
你贴的这段代码属于未定义行为,编译器没报错是因为C++标准没强制要求检测这种跨容器迭代器的问题,不是行为合理:
std::remove_if操作的是isValidToRemove的元素,返回的迭代器也是属于这个容器的。- 但你把这个迭代器传给了
valsToRemove.erase(),而erase要求传入的迭代器必须属于调用它的容器。这种违反前置条件的操作,C++标准不做任何保证——可能输出乱码、崩溃,或者像你看到的出现随机元素,所有结果都是不确定的。
二、利用外部标记向量删除元素的简洁写法
不用写低效的for循环,有两种简洁的实现方式:
方法1:带索引追踪的erase-remove变种
直接在std::remove_if的谓词里通过索引关联两个容器:
#include <vector> #include <algorithm> #include <iostream> int main() { std::vector<int> valsToRemove{4, 5, 6, 2, 3, 7, 10}; std::vector<int> isValidToRemove{0, 1, 0, 1, 1, 0, 1}; // 先确保两个容器大小一致,避免越界 if (valsToRemove.size() != isValidToRemove.size()) { return 1; } size_t idx = 0; auto it = std::remove_if(valsToRemove.begin(), valsToRemove.end(), [&](const int&) { // isValidToRemove里0表示要删除对应元素 return isValidToRemove[idx++] == 0; }); valsToRemove.erase(it, valsToRemove.end()); for (const auto& val : valsToRemove) { std::cout << val << "\t"; } // 输出结果:5 2 3 10 return 0; }
如果是C++14及以上版本,还可以把索引直接写在lambda的捕获里,更简洁:
auto it = std::remove_if(valsToRemove.begin(), valsToRemove.end(), [&, idx = 0](const int&) mutable { return isValidToRemove[idx++] == 0; });
方法2:用std::copy_if生成新容器(不修改原容器)
如果需要保留原容器,直接生成过滤后的新容器:
std::vector<int> filteredVals; filteredVals.reserve(valsToRemove.size()); // 预分配空间提升性能 size_t idx = 0; std::copy_if(valsToRemove.begin(), valsToRemove.end(), std::back_inserter(filteredVals), [&](const int&) { // isValidToRemove里1表示保留对应元素 return isValidToRemove[idx++] == 1; });
关键注意点
一定要保证valsToRemove和isValidToRemove的大小完全相同,否则索引越界会再次触发未定义行为。
内容的提问来源于stack exchange,提问作者Gaurav Dhir
相关产品推荐
相关产品推荐

