64

Suppose I have the following code:

#include <vector>
struct A {
    int a;
    int x;
};
int main() {
    using namespace std;
    A a1;
    A a2;
    vector<A> va;
    va.push_back(a1);
    va.push_back(move(a2));
}

I am aware that the elements of std::vector are stored contiguously, unlike a std::list. In the above code a2 is moved but is there really no copying of a2 to the vector va? What is the difference between va.push_back(a2); and va.push_back(move(a2));?

Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
ggg
  • 1,857
  • 2
  • 21
  • 33
  • 8
    In your case, `std::move`ing `a2` does exactly *nothing*, since it's a flat type (i.e., it has no external data) and will still just copy. – Xeo Jul 20 '12 at 03:59
  • 2
    You may want to read [Can someone please explain move semantics to me?](http://stackoverflow.com/questions/3106110/) for an introduction to move semantics. – fredoverflow Jul 20 '12 at 07:45

3 Answers3

64

In your case, there is no effective difference, since you are using compiler-provided copy constructors. You would see a noticeable performance difference when using objects that are move-constructible, and take a lot of effort to copy. In that case, using push_back(x) would create a copy of the object, while push_back(move(x)) would tell push_back() that it may "steal" the contents of x, leaving x in an unusable and undefined state.

Consider if you had a vector of lists (std::vector<std::list<int> >) and you wanted to push a list containing 100,000 elements. Without move(), the entire list structure and all 100,000 elements will be copied. With move(), some pointers and other small bits of data get shuffled around, and that's about it. This will be lots faster, and will require less overall memory consumption.

cdhowie
  • 158,093
  • 24
  • 286
  • 300
  • 2
    Why? move c-tor will be generated automatically, not so? – ForEveR Jul 20 '12 at 04:09
  • 8
    @ForEveR It doesn't matter whether one is generated automatically or not, because there are no allocations in the `A` struct that can be moved. You have only two `int`s, and the move constructor will do the same thing that the copy constructor will do: assign the values stored in the ints on the source object to the new object. There is no possibility for optimization in the move scenario with this type, because it is already as optimal as it can get. – cdhowie Jul 20 '12 at 04:11
  • 1
    @cdhowie So something will be always be copied during a move? – ggg Jul 20 '12 at 04:39
  • 8
    @ggg: "move" is not some kind of magic salve you pour on your code that makes things not happen. What "move" does is allow you to specify transfer of ownership when a copy operation would have required allocation of new memory and so forth. If your copy wouldn't do anything like that, then a move isn't really helpful. – Nicol Bolas Jul 20 '12 at 06:09
  • if we push back a string into a `vector vec`, does `vec.push_back(move(str))` is faster than `vec.push_back(str)`? – Yves Nov 02 '16 at 20:10
  • 1
    @Thomas Possibly. Depending on whether certain optimizations are present (small string optimization, for example) it might not be faster -- but it won't be slower. Note, however, that after `vec.push_back(move(str))`, the `str` object is left in an unspecified state. – cdhowie Nov 03 '16 at 18:30
29

When you use va.push_back(a2) version vector<T>::push_back(const T&) will be called, when you use va.push_back(move(a2)) version vector<T>::push_back(T&&) will be called...

But in your case there is no difference for perfomance, since

15 The implicitly-defined copy/move constructor for a non-union class X performs a memberwise copy/move of its bases and members.

Paragraph 12.8 n3337 draft.

ForEveR
  • 55,233
  • 2
  • 119
  • 133
-1

I want to note something that other answers didn't have gone over; is that the ?.push_back(move(?)) will be slower than ?.push_back(?) in your case (when you have trivially copyable objects), because move constructor need to zero\set the moved object, which effectively you are writing\copying two objects.

LyingOnTheSky
  • 2,844
  • 1
  • 14
  • 33
  • 8
    A move constructor is not required to do anything to the moved object. It does not need to zero anything unless there are moved pointers that need to be reset to null. (Compiler-generated move constructors are not going to blanket-zero the source object.) – cdhowie Sep 12 '17 at 13:54