Recently I have found this weird behavior of unordered_set caused by erase
method. I present the minimal example below.
First I create an unordered_set. Then I erase one of the element, say France. Then I erase every element with a for loop. Upon execution, it segfaults. However, if I comment out the erasing France part, then the code works fine.
This program is compiled with g++ test.cpp --std=c++11
. The version of g++ is 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;
}
Does anyone have a clue?
Thanks, KC
Erasing elements inside a range-based for loop is undefined behavior. When you erase an element in a set, iterators to that element are invalidated, and behind the scenes the compiler uses the current element's iterator to progress to the next element. Range based for is equivalent to:
At the time
++__begin
is called, the element has been erased and the iterator is invalid.Edit: Here's an example of how you could do this correctly:
In C++11 the
erase
method returns a new iterator, so this avoids incrementing the old iterator after the element it points to has been erased. But also please note that this code is pretty meaningless, unless it's just an experiment. If you just want to clear a set's contents, callmyset.clear()
.