Decrementing iterator after each loop iteration shows weird behavior

Viewed 125

I created a program to try to practice on the semantics of the list data structure. I noticed a weird difference in the following pieces of code:

First code:

#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);
    }
}

Second code:

#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--);
    }
}

The objective of both pieces of code is simple - to simply erase all the elements in a list.

The only difference between them is the place where the list iterator is decremented. In the first code sample, I used the for-loop control flow to decrement the iterator. In the second, I used the post-decrement operator to decrement the iterator.

Based on my understanding, the above code samples should be equivalent because I decrement the iterator immediately after I erase an element from the list. Furthermore, according to the STL docs, only the iterator to the erased element in the list is invalidated. So there should not be any undefined behavior.

The problem is, the second code sample works as expected - it stops after erasing all elements in the list. However, for the first sample, the list size could even become negative?! When I tried increasing the initial number of elements in the list, the first program crashes halfway.

Could someone kindly advise me on why these code samples behave differently?

1 Answers

The 1st code has undefined behavior. As you said, erase makes the iterator invalid, the it-- evaluated after that leads to UB.

The 2nd code is fine; note the evaluation order is different. it-- will decrement the iterator, then return the original value (that's the point of post-decrement operator). The original value is passed to erase later. Decrement happens before erase so it's fine.

Related