我参考了这个问题:什么是拷贝交换惯用语?
实际上,上面的答案引导出以下实现:
class MyClass
{
public:
friend void swap(MyClass & lhs, MyClass & rhs) noexcept;
MyClass() { /* to implement */ };
virtual ~MyClass() { /* to implement */ };
MyClass(const MyClass & rhs) { /* to implement */ }
MyClass(MyClass && rhs) : MyClass() { swap(*this, rhs); }
MyClass & operator=(MyClass rhs) { swap(*this, rhs); return *this; }
};
void swap( MyClass & lhs, MyClass & rhs )
{
using std::swap;
/* to implement */
//swap(rhs.x, lhs.x);
}
请注意,我们可以完全避免使用swap(),而改为执行以下操作:
class MyClass
{
public:
MyClass() { /* to implement */ };
virtual ~MyClass() { /* to implement */ };
MyClass(const MyClass & rhs) { /* to implement */ }
MyClass(MyClass && rhs) : MyClass() { *this = std::forward<MyClass>(rhs); }
MyClass & operator=(MyClass rhs)
{
/* put swap code here */
using std::swap;
/* to implement */
//swap(rhs.x, lhs.x);
// :::
return *this;
}
};
注意,这意味着我们将不再在MyClass的std::swap中拥有有效的参数依赖查找。简而言之,是否有使用swap()方法的优势?
编辑:
我意识到上面第二个实现中有一个可怕的错误,而且它相当重要,所以我会将其保留原样以指导任何遇到此问题的人。
如果操作符=被定义为
MyClass2 & operator=(MyClass2 rhs)
那么只要rhs是一个右值,移动构造函数就会被调用。但是这意味着在使用以下语句时:
MyClass2(MyClass2 && rhs)
{
//*this = std::move(rhs);
}
需要注意的是,当调用operator=时,会导致对移动构造函数的递归调用...
这很微妙,很难发现,直到你遇到运行时堆栈溢出。
现在解决这个问题的方法是同时定义
MyClass2 & operator=(const MyClass2 &rhs)
MyClass2 & operator=(MyClass2 && rhs)
这使我们能够定义拷贝构造函数为
MyClass2(const MyClass2 & rhs)
{
operator=( rhs );
}
MyClass2(MyClass2 && rhs)
{
operator=( std::move(rhs) );
}
请注意,您需要编写相同数量的代码,复制构造函数是“免费的”,您只需要编写operator=(&)而不是复制构造函数,以及operator=(&&)而不是swap()方法。
*this = std::forward<MyClass>(rhs); /* that should be 'move', btw */
代替swap(*this, rhs);
有什么好处?你只是延迟了调用。 - Xeostd::forward<T>
中的T实际上可以更改;而在这里它总是MyClass
)。只需使用std::move
即可。是的,这似乎更加简洁,我个人喜欢将赋值和交换分开(后者恰好使用前者)。 - GManNickG