我想在迭代容器时修改它。但是第一个版本的代码没有正常工作。谁能解释一下原因吗?
// 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());
}
}
发布于 2020-01-09 15:25:11
看看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
以获得相同的结果。
通常,对于特定情况有一种更简单的方法,如上面所描述的,但如果没有,通常在迭代时修改容器的正确方法是不使用范围,而是使用显式迭代器循环,并使用操作的结果(无论是擦除还是插入)作为下一个迭代器值,而不是增加现在无效的迭代器。
发布于 2020-01-09 15:43:10
我不认为第二个解决方案有什么问题,您可以使用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);
}
}
https://stackoverflow.com/questions/59667104
复制相似问题