gowrath gowrath - 1 month ago 14
C++ Question

C++ for each loop with mutating vector

I just had a quick question about C++. If someone else has already asked this please let me know and I'll remove it as a duplicate.

My question is essentially, what is the behavior of a for each loop if you are changing the vector as you iterate. For example:

int main() {
// your code goes here
std::vector<char> v;
for(char i = 'a'; i <= 'f'; i++) {
v.push_back(i);
}
// v is {a, b, c, d, e, f}
char c = 'A';
for(const auto &elem : v) {
cout << elem << ", ";
v.push_back(c++); // appending 'A', 'B', 'C', ...
}
cout << endl;
// outputs: "a, b, c, , , ," (note there isn't a ... here, the loop stops).


// Just printing normally now
cout << "--------" << endl;
for(const auto &elem : v) {
cout << elem << ", ";
}
cout << endl;
// outputs: a, b, c, d, e, f, A, B, C, D, E, F,
return 0;
}


Could anyone potentially explain this behavior? I know the for each above is equivalent to the code below but that doesn't seem to make things any clearer.

for (auto __begin = v.begin(), __end = v.end();
__begin != __end; ++__begin) {
auto elem = *__begin;
cout << elem << ", ";
v.push_back(c++); // appending 'A', 'B', 'C', ...
}
cout << endl;


Update:



This problem seems to be caused by the vector resizing which invalidates the iterators. Calling a
v.reserve(20)
after creating the vector fixes this and outputs:
a, b, c, d, e, f,
in the second floor loop.

Answer

the documentation for std::vector::push_back states

If the new size() is greater than capacity() then all iterators and references (including the past-the-end iterator) are invalidated. Otherwise only the past-the-end iterator is invalidated.

When the for loop increments, it compares the new iterator position to v.end() and probably goes into UB, probably some segfault or exception.