неизрасходованный for-l oop результат после изменения третьей части цикла for - PullRequest
0 голосов
/ 11 июля 2020

Когда я использую for-loop в исходном файле, я получаю неизрасходованный результат. Вот минимальный исходный файл (я скрываю заголовок файла и функцию print_set):

// the main function
int main(void) {
    set<int> test{3, 5};
    print_set(test);

    for (auto it = test.begin(); it != test.end();) {
        auto node = test.extract(it);
        ++it;
    }
    print_set(test);
}

Затем я использую команду для компиляции и запуска:

$ g++ --version
g++ (Dedian 8.3.0-6) 8.3.0
... (not very important infomation for this question)
$ g++ -std=c++17 temp.cpp
$ ./a.out
[ 3 5 ]
[ ]

Теперь все идет хорошо, но после того, как я изменил часть for-loop на эту (я заменил ++it на третью часть головы for-loop):

    for (auto it = test.begin(); it != test.end(); ++it) {
        auto node = test.extract(it);
    }

Теперь результат:

$ ./a.out
[ 3 5 ]
zsh: segmentation fault (core dumped) ./a.out

Zsh - это оболочка Linux, которую я использую, это не очень важная информация. После просмотра некоторых веб-страниц о for-loop, например, , и т.д. c., Я все еще не знаю, почему? Это ошибка? Почему они не равны и дают два разных результата? Потому что метод extract? Но почему первая часть может работать?

Спасибо.

1 Ответ

2 голосов
/ 11 июля 2020

Из документации std::set<T>::extract() :

Извлечение узла делает недействительными итераторы извлеченного элемента.

После вызова test.extract(it), итератор it больше не действует. Даже его увеличение или сравнение с другим итератором не являются определенными операциями. Единственные безопасные действия, которые вы можете сделать с it после извлечения:

  • Пусть он будет уничтожен.
  • Назначьте ему действительный итератор.

Все остальное вызывает неопределенное поведение, поэтому оба примера кода в вашем вопросе вызывают неопределенное поведение. Спрашивать, почему один делает что-то иное, чем другой, - бессмысленный вопрос. Каждый из них может делать что-то свое каждый раз при запуске программы, включая то, что вы хотели, чтобы код делал! так.

Чтобы исправить код, вам нужно будет скопировать итератор, увеличить оригинал, а затем извлечь копию. Именно это и делает оператор постинкремента:

for (auto it = test.begin(); it != test.end();) {
    auto node = test.extract(it++);
}

В этом примере it изменяется до того, как произойдет извлечение . Этот пример имеет четко определенное поведение и будет извлекать каждый элемент в наборе один за другим.

...