Подтвердить что ты не робот

Кража внутри конструктора перемещения

Во время реализации конструктора перемещения класса игрушек я заметил шаблон:

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;
}

Образец, очевидно, имеет вид:

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

Итак, я написал макрос препроцессора, чтобы сделать кражу менее сложной и подверженной ошибкам:

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

Теперь реализация выглядит следующим образом:

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

Есть ли недостатки в этом? Есть ли более чистое решение, которое не требует препроцессора?

4b9b3361

Ответ 1

Вот рекомендуемый шаблон:

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;
}

Естественно, если члены данных являются скалярными типами, std::move не требуется. Но если вы копируете этот шаблон вокруг, полезно включить move так или иначе, чтобы, когда данные элемента не являются скалярными, std::move не забывается.

Кроме того, если данные элемента имеют фактические конструкторы перемещения, вы можете просто опустить тело:

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

И если вы хотите обобщить типы, у которых нет конструкторов перемещения, но они имеют состояние по умолчанию, не основанное на ресурсах, вы можете:

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();
}

Я рекомендую упорядочить эти операторы в том же порядке, в котором они объявлены как члены данных в определении класса array2D. И я не нахожу ничего плохого в повторении списка инициализаторов в теле. Это необходимый и второй шаг. Нет необходимости подметать его под ковром.

Ответ 2

Как насчет использования template:

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

Использование:

MOVE(data_, that.data_);

@Fred, из вашего комментария, если вы хотите не упоминать элемент данных дважды, то:

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

Использование:

STEAL(data_);

Ответ 3

Инициализируйте свои собственные члены по умолчанию, а затем swap.

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

    this->swap(that);
}

Даже более чистый (если ваш компилятор поддерживает его)

array2D(array2D&& that)
: array2D() {
    this->swap(that);
}