Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does reallocating a vector copy instead of moving the elements? [duplicate]

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.

like image 690
Konrad Rudolph Avatar asked Apr 12 '12 16:04

Konrad Rudolph


People also ask

Does vector Push_back make a copy?

Yes, std::vector<T>::push_back() creates a copy of the argument and stores it in the vector.

Does vector make a copy?

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.

What is a vector copy?

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.

Does vector erase reallocate?

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.


1 Answers

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).

like image 96
Jerry Coffin Avatar answered Oct 13 '22 21:10

Jerry Coffin