Уменьшение итератора после каждой итерации цикла показывает странное поведение - PullRequest
4 голосов
/ 19 июня 2019

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

Первый код:

#include<iostream>
#include<list>

using namespace std;

int main() {
    list<int> l;
    int n = 100;
    for(int i = 0; i < n; i++) {
        l.push_back(i);
    }
    list<int>::iterator it = l.end();
    it--;
    for(; !l.empty(); it--) {
        cout << "the size of l is " << (int) l.size() << endl;
        l.erase(it);
    }
}

Второй код:

#include<iostream>
#include<list>

using namespace std;

int main() {
    list<int> l;
    int n = 100;
    for(int i = 0; i < n; i++) {
        l.push_back(i);
    }
    list<int>::iterator it = l.end();
    it--;
    for(; !l.empty();) {
        cout << "the size of l is " << (int) l.size() << endl;
        l.erase(it--);
    }
}

Цель обоих частей кодапросто - просто стереть все элементы в списке.

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

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

Проблема в том, что второй пример кода работает, как и ожидалось, - он останавливается после удаления всех элементов в списке.Однако для первого образца размер списка может даже стать отрицательным ?!Когда я попытался увеличить начальное количество элементов в списке, первая программа зависала на полпути.

Может кто-нибудь любезно посоветовать мне, почему эти примеры кода ведут себя по-разному?

1 Ответ

5 голосов
/ 19 июня 2019

1-й код имеет неопределенное поведение.Как вы сказали, erase делает итератор недействительным, it--, вычисленный после этого, приводит к UB.

2-й код в порядке;обратите внимание, что порядок оценки отличается.it-- будет уменьшать итератор, а затем возвращать исходное значение (это точка оператора после декремента).Исходное значение передается erase позже.Уменьшение происходит до erase, так что все в порядке.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...