0

we all know when use erase in for , we must reset iter, like iter = vector.erase(iter), since erase option will invalidate iteraor. however i found that, not reset also works, the code belows:

int main() {
    vector<int> a;
    a.push_back(1);
    a.push_back(2);
    a.push_back(3);
    a.push_back(2);
    a.push_back(10);
    a.push_back(11);
    for (vector<int>::iterator iter = a.begin(); iter != a.end();) {
        if (*iter == 2) {
            // iter = a.erase(iter); the same 
            a.erase(iter);
            continue;
        } else {
            iter++;
        }
    }
    for (vector<int>::iterator iter = a.begin(); iter != a.end(); iter++) {
        cout << *iter << " ";
    }
    cout << endl;
    return 0;
}

the code run successfully, with the output: 1 3 10 11.

so my question is while "a.erase(iter)" got the same result with "iter = a.earse(iter)" in this code?

celix
  • 63
  • 2
  • 3
    Undefined behavior. That is an aspect of C++ -- you do something that you shouldn't be doing, then the results are undefined. – PaulMcKenzie Aug 01 '15 at 03:54
  • 1
    And BTW, running your code using Visual Studio pops up a very scary "Debug Assertion" box when accessing the iterator that is invalid. – PaulMcKenzie Aug 01 '15 at 03:59
  • i run it in mac os 10 times, it all works fine. it confuse me, so i check the source code of libstdc++, however, i still can't figure out the reason. – celix Aug 01 '15 at 07:19
  • It doesn't really "work fine". Maybe in your toy program it "works", but put that same code in a larger program that does many other things with the vector, and all bets are off as to what can happen. Also, there is no need to be confused -- just don't write code like that which purposefully does wrong things. Very few programmers spend their time trying to figure out why their incorrect code "works". As long as you know it's wrong and produces undefined behavior, that is all that matters. – PaulMcKenzie Aug 01 '15 at 10:26

2 Answers2

3

What you are seeing is undefined behavior.

From http://en.cppreference.com/w/cpp/container/vector/erase (emphasis mine)

Removes specified elements from the container.
1) Removes the element at pos.
2) Removes the elements in the range [first; last).

Invalidates iterators and references at or after the point of the erase, including the end() iterator.

By using

a.erase(iter);
continue;

You are accessing an invalid iterator.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • i know it, i am just curious about the result. i run it in mac os, it always turn out the right result, so i begin to see the source code of libstdc++ , try to get the reason, but the source code is complicated, i can't figure out the reason – celix Aug 01 '15 at 07:14
  • @celix, unfortunately, that's the nature of undefined behavior, which includes seemingly sane behavior. – R Sahu Aug 02 '15 at 00:56
2

Your code relies on element reduction of your vector to not perform a reserve reduction to eliminate unneeded capacity. To be frank, nearly all standard library implementations I've used do this (reduce the size value and shift the elements to the "left"), but it is not guaranteed by the standard to behave this way. Rather, the exact opposite is the case. Once you erase the current iterator position, that iterator and any others past are invalid. Thus your code exhibits undefined behavior.

Regarding where the standard specifically says your iterator becomes invalid:

C++11 § 23.3.6.5 [vector.modifiers]

....

iterator erase(const_iterator position);

iterator erase(const_iterator first, const_iterator last);

  • Effects: Invalidates iterators and references at or after the point of the erase.
  • Complexity: The destructor of T is called the number of times equal to the number of the elements erased, but the move assignment operator of T is called the number of times equal to the number of elements in the vector after the erased elements.
  • Throws: Nothing unless an exception is thrown by the copy constructor, move constructor, assignment operator, or move assignment operator of T.

But I'm curious: Why aren't you just using the remove/erase idiom?:

a.erase(std::remove(a.begin(), a.end(), 2), a.end());
Community
  • 1
  • 1
WhozCraig
  • 65,258
  • 11
  • 75
  • 141