最近我发现了由erase
方法引起的这种奇怪的unordered_set行为。我提出下面的最小例子。
首先我创建一个unordered_set。然后我擦除其中一个元素,比如法国。然后我用for循环擦除每个元素。执行时,它会出现段错误。但是,如果我评论删除法国部分,那么代码工作正常。
此程序使用g++ test.cpp --std=c++11
编译。 g ++的版本是4.9.1。
#include <iostream>
#include <string>
#include <unordered_set>
int main ()
{
std::unordered_set<std::string> myset =
{"USA","Canada","France","UK","Japan","Germany","Italy"};
// erasing by key, causing segfault later; no segfault if commented out
myset.erase ( "France" );
std::cout << "myset contains:";
for ( const std::string& x: myset ) { myset.erase(x); }
// The problem persists for a regular for loop as well.
//for ( std::unordered_set<std::string>::iterator it = myset.begin(); it!=myset.end(); it++ ) { myset.erase(it); }
std::cout << std::endl;
return 0;
}
有没有人有线索?
谢谢, KC
答案 0 :(得分:6)
在基于范围的for循环内擦除元素是未定义的行为。擦除集合中的元素时,该元素的迭代器无效,并且在后台编译器使用当前元素的迭代器进入下一个元素。基于范围相当于:
auto && __range = range-init;
for ( auto __begin = begin-expr(__range),
__end = end-expr(__range);
__begin != __end;
++__begin ) {
for-range-declaration = *__begin;
statement
}
在调用++__begin
时,元素已被删除,迭代器无效。
编辑:以下是您如何正确执行此操作的示例:
auto it = myset.begin();
while (it != myset.end()) { it = myset.erase(it); }
在C ++ 11中,erase
方法返回一个新的迭代器,因此这可以避免在它指向的元素被删除后递增旧的迭代器。但请注意,这段代码毫无意义,除非它只是一个实验。如果您只想清除内容的内容,请致电myset.clear()
。