10

我确实搜索了互联网,发现了 3 种定义移动构造函数的方法:

  1. 依赖编译器:

    T(T&& other) = default;
    
  2. 解引用this指针:

    T(T&& other) {
      *this = std::move(other);
    }
    
  3. 显式重新分配所有成员:

    T(T&& other) {
      T.a = other.a;
      T.b = other.b;
      //...
    }
    

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

4

2 回答 2

17

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

T(T && rhs)
: a(std::move(rhs.a))
, b(std::move(rhs.b))
{  }

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

URM(URM && rhs)
: resource(rhs.resource)
{
    rhs.resource = nullptr;
}

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


这是不相关的,但是由于您提到了赋值运算符,因此这是流行的交换和分配/交换习语:

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

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

于 2013-08-10T14:34:01.223 回答
1
T(T&& other)
: data(0) // initialize members
{
    swap(other); // where the class has a member function swap or std::swap(this->data, other.data)
}
于 2013-08-10T14:40:06.067 回答