2

Could someone explain to me why this simple code:

#include <iostream>
#include <vector>

int main()
{
    std::vector<int> v = {0, 1, 2, 3, 4, 5};

    for(auto i: v)
    {
        std::cout << i << ' ';
    }

    std::cout << std::endl;

    for(auto i: v)
    {
        std::cout << i << ' ';
        v.push_back(4);
    }

    std::cout << std::endl;

    for(auto i: v)
    {
        std::cout << i << ' ';
    }
}

Code posted here: http://cpp.sh/5kxdu

Outputs:

0 1 2 3 4 5 
0 0 2 3 4 5 
0 1 2 3 4 5 4 4 4 4 4 4 

While I would expect:

0 1 2 3 4 5 
0 1 2 3 4 5 
0 1 2 3 4 5 4 4 4 4 4 4 

I know it's bad to modify the vector during the for loop (was just trying something else when I hit this). After v declaration, v.capacity() returns 6 and obviously, adding v.reserve(12) after v is declared fixes the issue. There is definitely something related to the vector reallocating itself to expand its capacity during the push_back operation.

But I'm wondering if that's just an undetermined behaviour and I was just lucky that only the second element was wrong or if this is something predictable and explainable (considering how the for loop works).

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
jpo38
  • 20,821
  • 10
  • 70
  • 151

3 Answers3

7

A ranged based for loop has the form of

{
    auto && __range = range_expression ; 
    for (auto __begin = begin_expr,
         __end = end_expr; 
         __begin != __end; ++__begin) { 
        range_declaration = *__begin; 
        loop_statement 
    } 
} 

and sets the begin and end iterators at the start of the loop. When you call push_back it always invalidates the end iterator and if size grows larger than the capacity then all iterators are invalidated.

So using push_back is going to cause undefined behavior as you are using iterators that are no longer valid.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
2

After the first push_back the vector reallocates its buffer and iterators in the ranged for loop are invalidated.

101010
  • 41,839
  • 11
  • 94
  • 168
1

This is because the iterator used by the for loop can be invalidated during the push back. Under the hood the vector uses a buffer, and the push_back can cause the array to be reallocated.

More references here: http://www.cplusplus.com/forum/general/5189/

What happens under the hood of vector::push_back memory wise?

Community
  • 1
  • 1
Greg Hilston
  • 2,397
  • 2
  • 24
  • 35