如何正确定义移动构造函数?

tex*_*uce 10 c++ c++11

我确实搜索了互联网并找到了3种定义移动构造函数的方法:

  1. 依靠编译器:

    T(T&& other) = default;
    
    Run Code Online (Sandbox Code Playgroud)
  2. 解引用this指针:

    T(T&& other) {
      *this = std::move(other);
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 明确重新分配所有成员:

    T(T&& other) {
      T.a = other.a;
      T.b = other.b;
      //...
    }
    
    Run Code Online (Sandbox Code Playgroud)

哪一个是正确的方法?(第二个甚至是正确的吗?)

Ker*_* SB 17

正确的通用方法是移动构造每个成员,但这就是取消版本所做的事情:

T(T && rhs)
: a(std::move(rhs.a))
, b(std::move(rhs.b))
{  }
Run Code Online (Sandbox Code Playgroud)

作为一个粗略的规则,如果这是您所需要的,则应该使用默认定义,如果您正在执行显式实现移动语义的操作,则应编写显式移动构造函数,例如唯一所有权资源管理器:

URM(URM && rhs)
: resource(rhs.resource)
{
    rhs.resource = nullptr;
}
Run Code Online (Sandbox Code Playgroud)

这是否合适的指标可能是您的类是否具有用户定义的析构函数.在该示例中,析构函数将释放托管资源,并且这必须仅发生一次,因此必须修改移动的对象.


这是无关的,但由于你提到赋值运算符,这里是流行的swap-and-assign/swap惯用法:

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;
}
Run Code Online (Sandbox Code Playgroud)

这种方法的优点在于,您只需要一个适用于临时和非临时工作的赋值运算符,在适当的时候使用移动构造,只要您的所有成员都经过精心设计,您也只需要一个单一swap功能.最重要的是,如果交换函数没有抛出(设计良好的类应该允许),那么你的赋值运算符不会抛出,因为所有可能的异常都会在调用站点发生.

  • @JoachimPileborg:不.这个例子可能是`unique_ptr`的*实现*. (3认同)
  • @texasbruce:如果你的所有班级成员都是理智的,并且可以单独移动,你可以而且应该使用默认的移动构造函数.在一个设计良好的类中,情况就是这样,事实上,*如果*你需要手动定义一个移动构造函数,那么该类本质上不应该做任何其他事情(除了管理一个资源之外) . (2认同)