2014-11-09 3 views
2

Недавно я нашел это странное поведение неупорядоченного_set, вызванного методом erase. Я представляю минимальный пример ниже.unordered_map erase segfault

Сначала я создаю неупорядоченный_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

ответ

6

Стирание элементов внутри диапазона на основе для цикла не определено поведение. Когда вы стираете элемент в наборе, итераторы этого элемента недействительны, а за кулисами компилятор использует итератор текущего элемента для перехода к следующему элементу. Диапазон основан на равносильна:

auto && __range = range-init; 
for (auto __begin = begin-expr(__range), 
    __end = end-expr(__range); 
    __begin != __end; 
    ++__begin) { 
    for-range-declaration = *__begin; 
    statement 
} 

В то время ++__begin называется, элемент был удален, а итератор является недействительным.

Edit: Вот пример того, как вы могли бы сделать это правильно:

auto it = myset.begin(); 
while (it != myset.end()) { it = myset.erase(it); } 

В C++ 11 метода erase возвращает новый итератор, так что это позволяет избежать приращения старого итератора после элемента он указует был удален. Но также обратите внимание, что этот код довольно бессмыслен, если только это не эксперимент. Если вы просто хотите очистить содержимое набора, вызовите myset.clear().

+0

Спасибо за ваш ответ, Джонатан. Я попытался заменить цикл, основанный на диапазоне, на обычный цикл, проблема все еще сохраняется. См. Мое редактирование оригинального сообщения. –

+0

@ K.Chen: Ваше редактирование делает то же самое, что и для диапазона, основанного на значении, т. Е. Полагается на недопустимый итератор. Я добавлю пример моего ответа о том, как вы можете обойти это. –

Смежные вопросы