Possible Duplicate:
How to enforce move semantics when a vector grows?
insert
, push_back
and emplace
(_back
) can cause a reallocation of a std::vector
. I was baffled to see that the following code copies the elements instead of moving them while reallocating the container.
#include <iostream> #include <vector> struct foo { int value; explicit foo(int value) : value(value) { std::cout << "foo(" << value << ")\n"; } foo(foo const& other) noexcept : value(other.value) { std::cout << "foo(foo(" << value << "))\n"; } foo(foo&& other) noexcept : value(std::move(other.value)) { other.value = -1; std::cout << "foo(move(foo(" << value << "))\n"; } ~foo() { if (value != -1) std::cout << "~foo(" << value << ")\n"; } }; int main() { std::vector<foo> foos; foos.emplace_back(1); foos.emplace_back(2); }
On my specific machine using my specific compiler (GCC 4.7) this prints the following:
foo(1) foo(2) foo(foo(1)) ~foo(1) ~foo(1) ~foo(2)
However, when deleting the copy constructor (foo(foo const&) = delete;
), the following (expected) output is generated:
foo(1) foo(2) foo(move(foo(1)) ~foo(1) ~foo(2)
Why is that? Would’t moving generally be more efficient, or at least not much less efficient, than copying?
It bears noting that GCC 4.5.1 does the expected thing – is this a regression in GCC 4.7 or is it some deviously clever optimisation because the compiler sees that my object is cheap to copy (but how?!)?
Also note that I made sure that this is caused by reallocation, by experimentally putting a foos.reserve(2);
in front of the insertions; this causes neither copy nor move to be executed.
Yes, std::vector<T>::push_back() creates a copy of the argument and stores it in the vector.
At the time of declaration of vector, passing an old initialized vector copies the elements of the passed vector into the newly declared vector. They are deeply copied.
Copy enables you to: define a vector of operands, copy the values or bit status of each operand within that vector, write those values or status into a corresponding vector of operands of the same length.
Use vector::erase . It will not reallocate memory. If your erased range does not extend to the end of the container, it WILL re-locate the end elements. That means the ending elements will be move d to their proper spot in memory which likely incurs a data copy.
The short answer is that I think @BenVoigt is basically correct.
In the description of reserve
(§23.3.6.3/2), it says:
If an exception is thrown other than by the move constructor of a non-CopyInsertable type, there are no effects.
[And the description of resize
in §23.3.6.3/12 requires the same.]
This means that if T is CopyInsertable, you get strong exception safety. To assure that, it can only use move construction if it deduces (by unspecified means) that move construction will never throw. There's no guarantee that either throw()
or noexcept
will be necessary or sufficient for that though. If T is CopyInsertable, it can simply choose to always use copy construction. Basically, what's happening is that the standard requires copy construction-like semantics; the compiler can only use move construction under the as-if rule, and it's free to define when or if it'll exercise that option.
If T is not CopyInsertable, reallocation will use move construction, but exception safety depends on whether T's move constructor can throw. If it doesn't throw, you get strong exception safety, but if it throws, you don't (I think you probably get the basic guarantee, but maybe not even that and definitely no more).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With