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
++__beginis 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
erasemethod 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().