Как вызвать стирание с помощью обратного итератора с использованием цикла for - программирование

Как вызвать стирание с помощью обратного итератора с использованием цикла for

Относительно ответа, приведенного здесь: Как вызвать стирание с помощью обратного итератора

Следующие результаты приводят к ошибке сегментации (при ++it) при компиляции в g++ 4.8.4 с -std = С++ 11. Я не понимаю ответа?

  std::map<int,int> testmap;
  testmap[0] = 1;
  for(auto it=testmap.rbegin(); it!=testmap.rend(); ++it) {
    testmap.erase( std::next(it).base() );
  }
4b9b3361

Ответ 1

После некоторого использования этой идиомы, я думаю, что изменение в цикле в ответе Jarod42 заключается в том, чтобы сделать вещи более безопасными и поддерживать типичные тонкости цикла for(;;):

for (auto it = testcont.rbegin(), nit = it; it != testcont.rend(); it = nit) {
    nit = next(it);

    // whatever... maybe a continue somewhere or maybe not

    if (WE_WANT_TO_ERASE(it)) {
        nit = decltype(it){ testcont.erase(std::next(it).base()) };
    }

    // whatever... maybe a continue somewhere or maybe not
}

Использование цикла в другом ответе слишком опасно. Если бы кто-то бездумно добавил a continue; где-то в цикле, не увеличивая сначала итератор, результатом будет бесконечный цикл. Поскольку, вначале, это может выглядеть как обычный цикл for(;;), я считаю, что это рано или поздно произойдет. Аналогичным образом, если в цикле есть ветки, и если одна из этих ветвей пренебрегает увеличением итератора, вводится другая ошибка. Наконец, если вы выполните erase(), вы должны быть уверены в continue, прежде чем увеличивать итератор, иначе у вас будет еще одна ошибка.

Используя модифицированный цикл выше, цикл можно обрабатывать так же, как обычный цикл for(;;). Трюк состоит в том, чтобы увеличить nit ( "следующий итератор" ) как первую строку тела цикла. Тогда вам не нужно беспокоиться. Единственный раз, когда вам нужно обновить nit, вы делаете erase(). Все остальное работает так, как можно было бы ожидать, что цикл for будет работать.

Последнее замечание: я первоначально задал вопрос о картах, но это будет корректно работать и для vector, list и т.д.

Ответ 2

erase отменяет итератор, вы должны восстановить его из возврата стирания:

it = std::map<int,int>::reverse_iterator(testmap.erase( std::next(it).base() ));

или (С++ 11)

it = decltype(it){testmap.erase( std::next(it).base() )};

Демо.

Для полноты, вот как выглядит исправленный цикл из исходного вопроса (обратите внимание, что итерация удалена из for(...):

for (auto it = testmap.rbegin(); it != testmap.rend(); /* empty */) {
    if (WE_WANT_TO_ERASE(it)) {
        it = decltype(it){ testmap.erase(std::next(it).base()) };
    } else {
        ++it;
    }
}