C++ 在移动构造函数中偷窃

C++ 在移动构造函数中偷窃,c++,c++11,c-preprocessor,move-semantics,move-constructor,C++,C++11,C Preprocessor,Move Semantics,Move Constructor,在toy类的move构造函数的实现过程中,我注意到一个模式: 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_ =

在toy类的move构造函数的实现过程中,我注意到一个模式:

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

这有什么坏处吗?是否有不需要预处理器的更干净的解决方案?

使用
模板如何:

template<typename T> inline
void MOVE(T &dst, T &src)
{
  dst = src;
  src = 0;
}
@Fred,根据您的评论,如果您想避免两次提及数据成员,那么:

#define STEAL(X) MOVE(X, that.X)
用法:

MOVE(data_, that.data_);
STEAL(data_);

将您自己的成员初始化为默认值,然后交换
swap

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

    this->swap(that);
}
甚至更干净(如果编译器支持)


以下是推荐的模式:

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
类定义中声明为数据成员的顺序对这些语句进行排序。我发现在身体里重复初始化列表没有什么错。这是必要的第二步。没有必要把它藏起来。

这是偷窃,不是偷窃。:-)我还没有进入C++0x编程,但在我看来,复制工作应该由构造函数完成,可能是一个POD状态对象,然后清除整个状态对象,或者以某种方式为“其他”设置僵尸或默认状态?@Alf:这是一个已建立的术语吗?在这种情况下,我会考虑重命名它。@ FredOverflow:我不知道如何建立,但这是在早期讨论移动语义时经常被提到的术语。我确信“窃取”是MSVC团队使用的术语。它是有效的。
模板内联无效移动
怎么样?@fredwolflow,提到两次应该可以,因为
的参数名以后可能会被重命名。不过我也为这种情况编辑了答案。命名宏
\u MOVE
不是一个好主意,因为以下划线开头,后跟大写字母的标识符是为实现保留的。命名宏
MOVE\u
不是一个好主意,因为包含两个连续下划线的标识符是为实现保留的。(SCNR)命名函数
MOVE
是个坏主意,因为宏通常使用大写标识符。只需命名宏
MOVE()
和函数
MOVE()
。我想了想,但这不是有点低效吗?如果没有专门的
swap
,这将不起作用,因为C++0x
std::swap
移动对象,导致无限递归…@Alf:编译器可以轻松处理这个问题@约翰尼斯:我显式地调用了一个成员函数,而不是
std::swap
。提供交换操作是执行许多语义(包括复制)的最佳方法,因此期望提供交换操作并非不合理——即使只移动类也是可交换的。如果这样做,为什么不将其委托给默认构造函数?@DeadMG:following@JohannesD remark-->很明显,在C++0x中,您应该完全实现move构造函数或swap,并且可以根据您实现的构造函数编写另一个构造函数(除非您更愿意做两次……)。虽然,我确实想知道,似乎最好通过“自动”资源来处理。
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();
}