この回答に関する重要な更新/警告!
実際には、以下の合理的な現実世界のコードでダングリング参照を静かに作成する説得力のある例があります。追加の一時ファイルが作成されるという犠牲を払っても、他の回答の手法を使用してこの問題を回避してください。今後の参考のために、この回答の残りの部分はそのままにしておきます。
可換ケースの正しいオーバーロードは次のとおりです。
T operator+( const T& lhs, const T& rhs )
{
T nrv( lhs );
nrv += rhs;
return nrv;
}
T&& operator+( T&& lhs, const T& rhs )
{
lhs += rhs;
return std::move( lhs );
}
T&& operator+( const T& lhs, T&& rhs )
{
rhs += lhs;
return std::move( rhs );
}
T&& operator+( T&& lhs, T&& rhs )
{
lhs += std::move( rhs );
return std::move( lhs );
}
それはなぜですか、どのように機能しますか? まず、右辺値参照をパラメーターとして使用する場合、それを変更して返すことができることに注意してください。それが由来する式は、 を含む完全な式の終了前に右辺値が破棄されないことを保証する必要がありますoperator+
。これは、式が完全に評価されて一時変数 (ravlues) が破棄される前に、呼び出し元が (同じ式の一部である)operator+
の結果を使用する必要があるため、単純に右辺値参照を返すことができることも意味します。operator+
2 番目の重要な観察結果は、これにより一時的な操作と移動操作がさらに節約されることです。次の式を検討してください。
T a, b, c, d; // initialized somehow...
T r = a + b + c + d;
上記の場合、次と同等です。
T t( a ); // T operator+( const T& lhs, const T& rhs );
t += b; // ...part of the above...
t += c; // T&& operator+( T&& lhs, const T& rhs );
t += d; // T&& operator+( T&& lhs, const T& rhs );
T r( std::move( t ) ); // T&& was returned from the last operator+
これを他のアプローチで何が起こるかを比較してください。
T t1( a ); // T operator+( T lhs, const T& rhs );
t1 += b; // ...part of the above...
T t2( std::move( t1 ) ); // t1 is an rvalue, so it is moved
t2 += c;
T t3( std::move( t2 ) );
t3 += d;
T r( std::move( t3 );
つまり、まだ 3 つの一時ファイルがあり、それらはコピーされるのではなく移動されますが、上記のアプローチは一時ファイルを完全に回避するのにはるかに効率的です。
のサポートを含む完全なライブラリについては、 df.operatorsnoexcept
を参照してください。そこには、非可換ケースと混合型の操作のバージョンもあります。
これをテストするための完全なテスト プログラムを次に示します。
#include <iostream>
#include <utility>
struct A
{
A() { std::cout << "A::A()" << std::endl; }
A( const A& ) { std::cout << "A::A(const A&)" << std::endl; }
A( A&& ) { std::cout << "A::A(A&&)" << std::endl; }
~A() { std::cout << "A::~A()" << std::endl; }
A& operator+=( const A& ) { std::cout << "+=" << std::endl; return *this; }
};
// #define BY_VALUE
#ifdef BY_VALUE
A operator+( A lhs, const A& rhs )
{
lhs += rhs;
return lhs;
}
#else
A operator+( const A& lhs, const A& rhs )
{
A nrv( lhs );
nrv += rhs;
return nrv;
}
A&& operator+( A&& lhs, const A& rhs )
{
lhs += rhs;
return std::move( lhs );
}
#endif
int main()
{
A a, b, c, d;
A r = a + b + c + d;
}