我确实搜索了互联网并找到了3种定义移动构造函数的方法:
依赖于编译器:
T(T&& other) = default;
取消引用this
指针:
T(T&& other) {
*this = std::move(other);
}
明确重新分配所有成员:
T(T&& other) {
T.a = other.a;
T.b = other.b;
//...
}
哪一个是正确的方法? (第二个甚至是正确的吗?)
答案 0 :(得分:16)
正确的通用方法是移动构造每个成员,但这就是被解释的版本所做的事情:
T(T && rhs)
: a(std::move(rhs.a))
, b(std::move(rhs.b))
{ }
作为一个粗略的规则,如果这是你所需要的,你应该使用默认定义,如果你正在做一些显式实现移动语义的东西,你应该编写一个显式移动构造函数,例如独特的所有权资源管理器:
URM(URM && rhs)
: resource(rhs.resource)
{
rhs.resource = nullptr;
}
这是否合适的指标可能是您的类是否具有用户定义的析构函数。在示例中,析构函数将释放托管资源,这必须只发生一次,因此必须修改移动的对象。
这是无关的,但既然你提到赋值运算符,这里是流行的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;
}
这种方法的优点在于,您只需要一个适用于临时和非临时工作的赋值运算符的单个版本,在适当的时候使用移动构造,并且只要您的所有成员都经过精心设计,您也可以只需要一个swap
函数。最重要的是,如果交换函数没有抛出(设计良好的类应该允许),那么你的赋值运算符不会抛出,因为所有可能的异常都会在调用站点发生。
答案 1 :(得分: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)
}