无序映射擦除segfault

unordered_map erase segfault

本文关键字:segfault 擦除 映射 无序      更新时间:2023-10-16

最近我发现了由erase方法引起的无序集的这种奇怪行为。我在下面举一个最简单的例子。

首先,我创建了一个无序集。然后我抹掉其中一个元素,比如法国。然后我用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

清除基于范围的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()