Кража внутри конструктора перемещения - PullRequest
12 голосов
/ 27 июня 2011

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

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

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

Ответы [ 3 ]

12 голосов
/ 27 июня 2011

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

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.И я не нахожу ничего плохого в повторении списка инициализаторов в теле.Это является необходимым и вторым шагом.Нет необходимости подмести его под ковром.

9 голосов
/ 27 июня 2011

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

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

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

MOVE(data_, that.data_);

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

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

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

STEAL(data_);
5 голосов
/ 27 июня 2011

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

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

    this->swap(that);
}

Еще чище (если ваш компилятор поддерживает это)

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