我想在遍历它的同时修改我的容器。但是该代码的第一个版本无法正常运行。有人可以解释原因吗?
// version 1
int main(){
int a=1, b=2, c=5;
std::set<int*> m = {&a, &b, &c};
for(auto mi : m){
std::cout << *mi << std::endl;
m.erase(mi);
}
}
然后我尝试了另一个版本。好像还好这样迭代是否有潜在的麻烦?
// version 2
int main(){
int a=1, b=2, c=5;
std::set<int*> m = {&a, &b, &c};
while (!m.empty()){
std::cout << **m.begin() << std::endl;
m.erase(m.begin());
}
}
答案 0 :(得分:5)
看看std::set::erase
的文档:
对已删除元素的引用和迭代器无效。
这不足为奇。
现在,考虑一下基于范围的for循环的简写:
auto && range = range_expression ;
for (auto begin = begin_expr, end = end_expr; begin != end; ++begin) {
range_declaration = *begin;
loop_statement
}
通过循环增量,通知迭代后如何使用当前元素的迭代器。递增无效迭代器的行为是不确定的。
在第二个循环中,始终通过调用std::set::begin
获得有效的迭代器。
在这种特殊情况下,在迭代时修改容器是没有意义的。编写一个循环以打印所有元素,然后调用clear
获得相同的结果。
通常,在某些特定情况下,例如上文所述,有一种更简单的方法,但是如果没有,通常,在迭代时修改容器的正确方法是不使用range-for而是使用显式迭代器循环并使用操作结果(擦除或插入)作为下一个迭代器值,而不是递增现在无效的迭代器。
答案 1 :(得分:1)
第二种解决方案没有发现任何问题,不过您可以使用set
iterator
:
#include <iostream>
#include <set>
using namespace std;
int main(){
int a=1, b=2, c=5;
set<int*> m = {&a, &b, &c};
set<int*>::iterator it = m.begin();
while(it != m.end()){
cout << **it << endl;
it = m.erase(it);
}
}