For those interested, here's a test program with which you can play. The results are, respectively, 0+1, 3+0, 0+3, 0+3, 0+0. #include <iostream> #include <utility> int copies = 0; int moves = 0; struct T { int v_; explicit T( int v ): v_( v ) {} T( T const& t ): v_( t.v_ ) { ++copies; } T( T&& t ): v_( t.v_ ) { ++moves; } T& operator+=( T const& t ) { v_ += t.v_; return *this; } }; T operator+( const T& lhs, const T& rhs ) { T nrv( lhs ); nrv += rhs; return nrv; } // v1 T&& operator+( T&& lhs, const T& rhs ) { lhs += rhs; return std::move( lhs ); } // v2 T&& operator+( const T& lhs, T&& rhs ) { rhs += lhs; return std::move( rhs ); } // v3, remember: we assume a commutative + T&& operator+( T&& lhs, T&& rhs ) { lhs += rhs; return std::move( lhs ); } // v4 /* T operator+( const T& lhs, const T& rhs ) { T nrv( lhs ); nrv += rhs; return nrv; } // v1 T operator+( T&& lhs, const T& rhs ) { lhs += rhs; return lhs; } // v2 T operator+( const T& lhs, T&& rhs ) { rhs += lhs; return rhs; } // v3, remember: we assume a commutative + T operator+( T&& lhs, T&& rhs ) { lhs += rhs; return lhs; } // v4 */ /* T operator+( const T& lhs, const T& rhs ) { T nrv( lhs ); nrv += rhs; return nrv; } // v1 T operator+( T&& lhs, const T& rhs ) { lhs += rhs; return std::move(lhs); } // v2 T operator+( const T& lhs, T&& rhs ) { rhs += lhs; return std::move(rhs); } // v3, remember: we assume a commutative + T operator+( T&& lhs, T&& rhs ) { lhs += rhs; return std::move(lhs); } // v4 */ /* T operator+( T lhs, const T& rhs ) { lhs += rhs; return lhs; } */ /* T operator+( T const & lhs, T const & rhs ) { return T( lhs.v_ + rhs.v_ ); } */ int main() { T t = T(1) + T(2) + T(3) + T(4); std::cout << "T(" << t.v_ << "): " << copies << " copies, " << moves << " moves\n"; }