在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下一次迭代中使用的元素。


以上是在C++stl映射中遍历时擦除会导致运行时错误的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>