SwiftMango
SwiftMango

Reputation: 15284

How to properly define a move constructor?

I did search the internet and found 3 ways of defining a move constructor:

  1. Relying on compiler:

    T(T&& other) = default;
    
  2. Dereference this pointer:

    T(T&& other) {
      *this = std::move(other);
    }
    
  3. Explicitly reassign all members:

    T(T&& other) {
      T.a = other.a;
      T.b = other.b;
      //...
    }
    

Which one is the proper way ? (And is the second one even correct?)

Upvotes: 11

Views: 770

Answers (2)

Kerrek SB
Kerrek SB

Reputation: 477020

The proper generic way is to move-construct each member, but that's what the defauted version does anyway:

T(T && rhs)
: a(std::move(rhs.a))
, b(std::move(rhs.b))
{  }

As a rough rule, you should use the default definition if this is all you need, and you should write an ex­pli­cit move constructor if you're doing something that ex­pli­citly implements move semantics, such as a unique-ownership resource manager:

URM(URM && rhs)
: resource(rhs.resource)
{
    rhs.resource = nullptr;
}

The indicator for whether this is appropriate is probably whether your class has a user-defined de­struc­tor. In the example, the destructor would release the managed resource, and this must happen only once, so the moved-from object must be modified.


This is unrelated, but since you are mentioning the assignment operator, here's the popular swap-and-assign/swap idiom:

void swap(URM & rhs) noexcept      // assume members are noexcept-swappable!
{
    using std::swap;
    swap(resource, rhs.resource);
    // ...
}

URM & operator=(URM rhs) noexcept  // pass by value
{
    rhs.swap(*this);
    return *this;
}

The beauty of this approach is that you only need one single version of the assignment operator that works for temporaries and non-temporaries alike, using move construction when appropriate, and as long as all your members are well-designed, you also only need one single swap function. On top of that, if the swap function doesn't throw (which a well-designed class should allow), then your assignment operator doesn't throw, since all the possible exceptions would already occur at the call site.

Upvotes: 18

user2249683
user2249683

Reputation:

T(T&& other)
: data(0) // initialize members
{
    swap(other); // where the class has a member function swap or std::swap(this->data, other.data)
}

Upvotes: 1

Related Questions