c++ copy constructor vs move constructor for std::vector

potuz picture potuz · Oct 1, 2017 · Viewed 7k times · Source

I have a class that holds a std::vector like

struct Mystruct
{
  Mystruct(const std::vector<int>& w): v(w) 
  {
    std::cout << "Copy constructor :" << v.at(0) << "\n";
  }
  Mystruct(const std::vector<int>&& w): v(w)
  {
    std::cout << "Move Constructor :" << v.at(0) << "\n";
  }
  private:
  std::vector<int> v;
};

And I create objects like

int main() 
{
  auto x = std::vector<int> {1,2,3};
  Mystruct M1(x);
  Mystruct M2(std::vector<int> {3,2,1});
  return 0; 
}

M1 is constructed using the copy constructor and M2 using the "move" constructor, however running in gdb both assignements keep different addresses for v and w, the same happens if I use v (std::move(w)) in the initialization list of the second constructor. so I guess that both assignement are copying the contents of w, is this correct? if its the case, how can I make to move the contents of w instead of copying them

Answer

Benjamin Lindley picture Benjamin Lindley · Oct 1, 2017

First of all, moving an object does not change its address. You can't, by definition, change the address of an object, since an address is part of the definition of an object. If you have a different address, you have a different object. What moving does (at least in regards to classes in the standard library) is transfer ownership of the dynamically allocated resources of an object.

Second, you're not moving anything. In order to move something, you have to call the move constructor. Which means you need to pass an r-value. A named object is never an r-value. So even though you have an r-value reference, you still need to cast it with std::move. But in your case, even that's not enough. In the standard library, all move constructors have signatures of Type(Type&&), never Type(const Type&&). If you pass a const value, even if you cast it with std::move, it will invoke the copy constructor, not the move constructor. In order to invoke the move constructor, your object must not be const.

So your constructor that moves a vector* should look like this:

Mystruct(std::vector<int>&& w): v(std::move(w))
{
    std::cout << "Move Constructor :" << v.at(0) << "\n";
}

And in order to compare addresses to see if it was actually moved, you should not be comparing the addresses of v and w (those will not, and cannot change), but v.data() and w.data(), which point to the dynamically allocated resources of the vector.

*not a move constructor. A move constructor would have the signature Mystruct(Mystruct&&) or Mystruct(const Mystruct&&)