c++stdvectorrange-based-loop

What would happen if I append elements in range based for?


I want to modify (push_back) element while iterate vector like this:

auto main() -> int {
    std::vector<double> v { 1, 2, 3 };

    for (auto& num : v) {
        std::cout << num << "\n";
        v.push_back(num);
    }

    std::cout << "===========\n";

    for (const auto& num : v) {
        std::cout << num << "\n";
    }
}

The output is:

1
1.00938e-320
0
===========
1
2
3
1
1.00938e-320
0

Why the output is not something like this:

1
2
3
===========
1
2
3
1
2
3

If range based for is just sugar of:

for (auto& it{v.begin()}, it != v.end(), it++)

Shouldn't there be two possible situations here?

  1. v.end() evalued every iterate and never end. It should be an infinite loop.

  2. v.end() evalued one time before iterate and should output as I expected:

1
2
3
===========
1
2
3
1
2
3

Solution

  • As you can see when you call std::vector::push_back:

    If after the operation the new size() is greater than old capacity() a reallocation takes place, in which case all iterators (including the end() iterator) and all references to the elements are invalidated. Otherwise only the end() iterator is invalidated.

    (emphasis is mine)

    Range-based loop is only a syntactic sugar for using the iterators.

    During push_back the vector might need to be resized and reallocated. In this case all iterators are invalidated including the one used as the "current" in the loop.
    Using it will lead to undefined-behavior.

    Even without reallocation you get into problems checking the end():
    It is queried once before the loop starts.
    There is a subtle difference from C++17. But in any case calling push_back inside the loop will invokes undefined-behavior because the end() queried before the loop is no longer valid.

    This means the standard gives no guarantee for the output of the program. Any output you observe is legitimate.