【问题标题】:unordered_map erase segfaultunordered_map 擦除段错误
【发布时间】:2014-11-09 20:54:12
【问题描述】:

最近我发现了这种由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

【问题讨论】:

    标签: c++ segmentation-fault unordered-set


    【解决方案1】:

    擦除基于范围的 for 循环内的元素是未定义的行为。当您擦除集合中的元素时,该元素的迭代器将失效,并且在幕后编译器使用当前元素的迭代器前进到下一个元素。基于范围的 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()

    【讨论】:

    • 感谢您的回复,乔纳森。我尝试用普通的for循环替换基于范围的for循环,问题仍然存在。请参阅我对原始帖子的编辑。
    • @K.Chen:您的编辑与基于范围的 for 所做的完全相同,即依赖无效的迭代器。我将在我的回答中添加一个示例,说明您如何解决这个问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-17
    • 1970-01-01
    • 2016-11-22
    • 1970-01-01
    相关资源
    最近更新 更多