2

I am trying to understand how C++ 11's for(type& x : ) (& stl's for_each) works internally and hoping someone can enlighten me. So the following code:

vector<int> v;
int z=0;
for (int i = 0; i < 5; ++i)
    v.push_back(z++);
for (int& i : v)
{
    printf(" %i", i);
    v.insert(v.begin(), z++);
}

prints 0 0 1 2 3. I would understand 0 0 0 0 0 or 0 1 2 3 4, but that output, I can't quite understand how? What does for(x:y) compile to?? Don't think this will matter much, but I am using clang 3.4.

Thanks!

user1181950
  • 779
  • 1
  • 9
  • 21
  • 2
    Your current code first inserts numbers, then iterates and inserts numbers. Is it intended? Remove the line `v.insert(v.begin(), z++);` from the second loop and try again. BTW. Why are you trying to modify a vector while looping over it? maybe you wanted to use std::list instead? – quetzalcoatl Mar 31 '14 at 22:42
  • You're seeing UB, I believe. – Oliver Charlesworth Mar 31 '14 at 22:45
  • std::vector::insert states besides other: "If the new size() is greater than capacity(), all iterators and references are invalidated.". The behavior is undefined. – wonko realtime Mar 31 '14 at 22:47
  • 1
    @wonko: That's not actually the rule that matters here, having a large capacity won't save you. The problem is that iterators after the insertion point are *always* invalidated. – Ben Voigt Mar 31 '14 at 22:48

1 Answers1

7

The C++11 Standard actually gives the equivalent traditional loop code, which is a pretty rare approach for Standardese. You'll find it in section 6.5.4:

enter image description here

enter image description here

In the expansion, it's clear that the end() value from before the loop starts is saved and checked later. Unfortunately, that iterator is invalidated by the first insert() call. The rule on iterator invalidation:

If no reallocation happens, all the iterators and references before the insertion point remain valid.

Clearly end() is not "before the insertion point".

Since the behavior of insert(p, rv) is "Inserts a copy of rv before p.", the insertion point is expressly before the iterator p (which is v.begin() here). So the loop iterator __begin is also not "before the insertion point" on the first pass.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720