在C++stl映射中遍历时擦除会导致运行时错误
以下 C++ 代码行给出了运行时错误,但如果删除了擦除操作,mymap.erase(v)则它可以工作:
map<int,int> mymap = {{1,0},{2,1},{9,2},{10,3},{11,4}};
for(auto it=mymap.rbegin();it!=mymap.rend();){
int v=it->first;
++it;
mymap.erase(v);
}
演示
这里迭代器it在删除它的 value 之前被改变v,所以it我相信迭代器应该不受影响。
回答
当您调用 时erase(v),您会使下一个(from ) 使用的基数 无效。因此,您需要从擦除值之前的基数创建一个新值。iteratorreverse_iterator++itreverse_iteratoriterator
此外,与其擦除所指的值,不如擦除reverse_iterator基数iterator,因为您已经知道要擦除哪个元素。没有必要map再次去寻找价值。
这对我有用:
map<int,int> mymap = {{1,0},{2,1},{9,2},{10,3},{11,4}};
for(auto it = mymap.rbegin(); it != mymap.rend(); ){
auto v = --(it.base());
v = mymap.erase(v);
it = map<int,int>::reverse_iterator(v);
}
演示
另一方面,这个循环本质上只是erase()从 'ing 所有元素mymap,所以更好的选择是使用mymap.clear()。
回答
确实,std::map::erase:
对被擦除元素的引用和迭代器无效。其他引用和迭代器不受影响。
但 std::reverse_iterator
对于由迭代器 i 构造的反向迭代器 r,关系
&*r == &*(i-1)始终为真(只要 r 是可解引用的);因此,一个反向迭代器由一个最后一个迭代器构造而成,它取消引用序列中的最后一个元素。
当您查看 cppreference 页面上的图像时,也许会更清楚。
关键部分是“反向迭代器将迭代器存储到下一个元素而不是它实际引用的元素”。
结果(对代码的小修改)
auto& element = *it; // fails in the next iteration, because...
int v = element.first;
++it; // now it stores an iterator to element
mymap.erase(v); // iterators to element are invalidated
您正在擦除it下一次迭代中使用的元素。