2

I am confused a bit. What I have learned or been told is that an iterator of a vector becomes invalid if erase is called. But why the code below works. It is compiled using g++ and run in Linux.

#include <vector>
#include <iostream>

using namespace std;

int main() {
  vector<int> vec;
  vec.push_back(1);
  vec.push_back(2);
  vec.push_back(3);

  vector<int>::iterator it = vec.begin();
  ++it;
  vector<int>::iterator it2;

  it2 = vec.erase(it);
  cout << "it: " << *it << endl;
  cout << "it2: " << *it2 << endl;
}

Thanks for any feedbacks!

Orunner
  • 404
  • 4
  • 13
  • 2
    It "works", that is it exhibits UB without visible symptoms. – PlasmaHH Feb 16 '12 at 11:13
  • Define "the code works". Doing what you expect is one possible outcome of _[Undefined Behavior](http://stackoverflow.com/a/1553407/140719)_. – sbi Feb 16 '12 at 11:15
  • When you use gcc, try running with `-D_GLIBCXX_DEBUG` and you will be enlightened. hopefully. – PlasmaHH Feb 16 '12 at 11:18
  • I know it is serious. I just found this bug in our legacy code. The system has run for years without having any problem. I am glad too that I found it now instead of having it crash in the production. :-) – Orunner Feb 16 '12 at 11:21

3 Answers3

2

From http://www.cplusplus.com/reference/stl/vector/erase/ (not the world's best C++ reference):

This invalidates all iterator and references to position (or first) and its subsequent elements.

So it is invalid; using it results in undefined behaviour. The fact that you happen to get what you expect is pure bad luck.

Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
  • 1
    Actually, I'd call it _bad_ luck. You are lucky when it crashes immediately, because then you realize that something is afoul. `:)` – sbi Feb 16 '12 at 11:16
  • thx your feedback. I thought about it but just could not believe it happened. – Orunner Feb 16 '12 at 11:17
1

What you're doing is Undefined Behaviour, and that it "works" is entirely accidental. You cannot and must not ever rely on this, because it could do just about anything. The behaviour of it is not defined.

Tony The Lion
  • 61,704
  • 67
  • 242
  • 415
0

As an implementation detail, vector<int>::iterator can easily be int*. I think in g++ it's a very thin wrapper around an int*. If that's the case, then erasing the middle element of a three-element vector means that the pointer data member of it is left pointing at the same address as the element removed, which of course will contain the value that previously was straight after it, and which is also validly referred to by it2.

The standard does not guarantee that it will still refer to anything, which is why you can't rely on the behavior you've observed here. But it explains what you've seen. An implementation pretty much has to go out of its way to make anything else happen when you dereference it. But compilers do go out of their way every day: debugging versions of the libraries for example, and some optimization techniques rely on assumptions that your code is correct.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699