Important update / warning about this answer!
Actually there is a convincing example which silently creates a freezing link in reasonable real code with the below. Please use another's response technique to avoid this problem, even at the cost of creating additional time periods. I will leave the rest of this answer untouched for future reference.
The correct overloads for the commutative case are:
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 ); }
Why is it and how does it work? First, note that if you take the rvalue reference as a parameter, you can change it and return it. The expression in which it proceeds must ensure that the rvalue value is not destroyed until the end of the full expression, including operator+ . This also means that operator+ can simply return a rvalue reference, since the caller needs to use the result of operator+ (which is part of the same expression) before the expression is fully evaluated and the temporary (ravlues) are destroyed.
A second important point is how it saves even more temporary and moving operations. Consider the following expression:
T a, b, c, d;
with the above, this is equivalent to:
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+
compare this to what happens with the other approach:
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 );
which means that you still have three temporary files, and although they are moved rather than copied, the approach given above is much more efficient in avoiding temporary situations.
For a complete library, including support for noexcept , see df.operators . There you will also find versions for non-commutative cases and operations on mixed types.
Here is the full test program for testing:
#include <iostream>