问题
Inspired by this question, asking how to append a vector to itself, my first thought was the following (and yes, I realize insert
is a better option now):
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
int main() {
std::vector<int> vec {1, 2, 3};
std::copy (std::begin (vec), std::end (vec), std::back_inserter (vec));
for (const auto &v : vec)
std::cout << v << ' ';
}
However, this prints:
1 2 3 1 * 3
The * is a different number every time the program is run. The fact that it's only the 2 being replaced is peculiar, and if there actually is an explanation for that, I'd be interested to hear it. Continuing, if I append to a different vector (a copy of the original), it outputs correctly. It also outputs correctly if I add the following line before the copy
one:
vec.reserve (2 * vec.size());
I was under the impression std::back_inserter
was a safe way to add elements onto the end of a container, despite not reserving memory beforehand. If my understanding is correct, what's wrong with the copying line?
I assume it's nothing to do with the compiler, but I'm using GCC 4.7.1.
回答1:
std::back_inserter
creates an inserting iterator that inserts elements into a container. Each time this iterator is dereferenced, it calls push_back
on the container to append a new element to the container.
For a std::vector
container, a call to push_back
where v.size() == v.capacity()
will result in a reallocation: a new array is created to store the contents of the vector, its current contents are copied into the new array, and the old array is destroyed. Any iterators into the vector at this time are invalidated, meaning they can no longer be used.
In your program, this includes the input range defined by begin(vec)
and end(vec)
from which the copy
algorithm is copying. The algorithm continues to use these iterators, even though they are invalidated, thus your program exhibits undefined behavior.
Even if your container had sufficient capacity, its behavior would still be undefined: the specification states that, upon insertion, "if no reallocation happens, all the iterators and references before the insertion point remain valid" (C++11 §23.3.6.5/1).
The call to push_back
is equivalent to insertion at the end, so the end iterator (std::end(vec)
) that you passed into std::copy
is invalidated after a single call to push_back
. If the input range is nonempty, the program therefore exhibits undefined behavior.
Note that the behavior of your program would be well-defined if you used a std::deque<int>
or a std::list<int>
, because neither of those containers invalidates iterators when elements are appended.
来源:https://stackoverflow.com/questions/11511510/wrong-results-when-appending-vector-to-itself-using-copy-and-back-inserter