gowrath gowrath - 1 year ago 128
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 Source

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.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download