Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stealing inside the move constructor

During the implementation of the move constructor of a toy class, I noticed a pattern:

array2D(array2D&& that)
{
    data_ = that.data_;
    that.data_ = 0;

    height_ = that.height_;
    that.height_ = 0;

    width_ = that.width_;
    that.width_ = 0;

    size_ = that.size_;
    that.size_ = 0;
}

The pattern obviously being:

    member = that.member;
    that.member = 0;

So I wrote a preprocessor macro to make stealing less verbose and error-prone:

#define STEAL(member) member = that.member; that.member = 0;

Now the implementation looks as following:

array2D(array2D&& that)
{
    STEAL(data_);
    STEAL(height_);
    STEAL(width_);
    STEAL(size_);
}

Are there any downsides to this? Is there a cleaner solution that does not require the preprocessor?

like image 583
fredoverflow Avatar asked Jun 27 '11 11:06

fredoverflow


People also ask

What is the purpose of move constructor in C++?

A move constructor enables the resources owned by an rvalue object to be moved into an lvalue without copying.

Why does Noexcept move a constructor?

Tagging our move constructor with "noexcept" tells the compiler that it will not throw any exceptions. This condition is checked in C++ using the type trait function: "std::is_no_throw_move_constructible". This function will tell you whether the specifier is correctly set on your move constructor.

Is move constructor automatically generated?

No move constructor is automatically generated.

How do I turn off move constructor?

To correct this, remove the move constructor completely. In the case of the class, once a copy constructor is present (user defined), the move is implicitly not generated anyway (move constructor and move assignment operator).


3 Answers

Here is the recommended pattern:

array2D(array2D&& that)
    : data_(std::move(that.data_)),
      height_(std::move(that.height_)),
      width_(std::move(that.width_)),
      size_(std::move(that.size_))
{
    that.data_ = 0;
    that.height_ = 0;
    that.width_ = 0;
    that.size_ = 0;
}

Naturally if the data members are scalar types, the std::move isn't needed. But if you're copying this pattern around, it is helpful to include the move anyway so that when the member data aren't scalar, the std::move doesn't get forgotten.

Also if the member data have actual move constructors, then you can simply omit the body:

array2D(array2D&& that)
    : data_(std::move(that.data_)),
      height_(std::move(that.height_)),
      width_(std::move(that.width_)),
      size_(std::move(that.size_))
{
}

And if you want to generalize to types that don't have move constructors, but do have a resource-less default constructed state, you can:

array2D(array2D&& that)
    : data_(std::move(that.data_)),
      height_(std::move(that.height_)),
      width_(std::move(that.width_)),
      size_(std::move(that.size_))
{
    that.data_ = Data();
    that.height_ = Height();
    that.width_ = Width();
    that.size_ = Size();
}

I recommend ordering these statements in the same order they are declared as data members in the array2D class definition. And I find nothing wrong with the repetition of the initializer list in the body. It is a necessary and second step. There is no need to sweep it under the rug.

like image 200
Howard Hinnant Avatar answered Sep 30 '22 01:09

Howard Hinnant


How about using template:

template<typename T> inline
void MOVE(T &dst, T &src)
{
  dst = src;
  src = 0;
}

Usage:

MOVE(data_, that.data_);

@Fred, from your comment, if you want to avoid mentioning data member twice, then:

#define STEAL(X) MOVE(X, that.X)

Usage:

STEAL(data_);
like image 32
iammilind Avatar answered Sep 30 '22 01:09

iammilind


Initialize your own members to default, and then swap.

array2D(array2D&& that)
{
    data_ = 0;    
    height_ = 0;    
    width_ = 0;    
    size_ = 0;

    this->swap(that);
}

Even cleaner (if your compiler supports it)

array2D(array2D&& that)
: array2D() {
    this->swap(that);
}
like image 20
Puppy Avatar answered Sep 29 '22 01:09

Puppy