私はこの質問を参照します: コピーアンドスワップイディオムとは何ですか?
事実上、上記の答えは次の実装につながります。
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() メソッドを使用する利点はあります。
編集:
上記の 2 番目の実装にはひどい間違いがあることに気付きました。これは非常に大きな問題なので、これに遭遇した人に説明するためにそのままにしておきます。
演算子 = が次のように定義されている場合
MyClass2 & operator=(MyClass2 rhs)
次に、rhs が右辺値である場合はいつでも、move コンストラクターが呼び出されます。ただし、これは次のことを意味します。
MyClass2(MyClass2 && rhs)
{
//*this = std::move(rhs);
}
operator= が move コンストラクターを呼び出すため、move コンストラクターへの再帰呼び出しになることに注意してください...
これは非常に微妙で、実行時のスタック オーバーフローが発生するまでは見つけるのが困難です。
これに対する修正は、両方を持つことです
MyClass2 & operator=(const MyClass2 &rhs)
MyClass2 & operator=(MyClass2 && rhs)
これにより、コピー コンストラクターを次のように定義できます。
MyClass2(const MyClass2 & rhs)
{
operator=( rhs );
}
MyClass2(MyClass2 && rhs)
{
operator=( std::move(rhs) );
}
同じ量のコードを記述し、コピー コンストラクターは「無料」で提供され、コピー コンストラクターの代わりに operator=(&) を記述し、swap() メソッドの代わりに operator=(&&) を記述するだけであることに注意してください。