Overloading on R-value references and code duplication

c++, c++11, rvalue-reference

Solution

Recycling temporaries is an interesting idea and you're not the only one who wrote functions that return rvalue references for this reason. In an older C++0x draft operator+(string&&,string const&) was also declared to return an rvalue reference. But this changed for good reasons. I see three issues with this kind of overloading and choice of return types. Two of them are independent of the actual type and the third argument refers to the kind of type that `vec` is.

Safety issues. Consider code like this:

vec a = ....;
vec b = ....;
vec c = ....;
auto&& x = a+b+c;

If your last operator returns an rvalue reference, `x` will be a dangling reference. Otherwise, it won't. This is not an artificial example. For example, the `auto&&` trick is used in the for-range loop internally to avoid unnecessary copies. But since the life-time extension rule for temporaries during reference binding does not apply in case of a function call that simply returns a reference, you'll get a dangling reference.

string source1();
string source2();
string source3();

....

int main() {
  for ( char x : source1()+source2()+source3() ) {}
}

If the last operator+ returned an rvalue reference to the temporary that is created during the first concatenation, this code would invoke undefined behaviour because the string temporary would not exist long enough.

In generic code, functions that return rvalue references force you to write

typename std::decay<decltype(a+b+c)>::type

instead of

decltype(a+b+c)

simply because the last op+ might return an rvalue reference. This is getting ugly, in my humble opinion.

Since your type `vec` is both "flat" and small, these op+ overloads are hardly useful. See FredOverflow's answer.

Conclusion: Functions with an rvalue reference return type should be avoided especially if these references may refer to short-lived temporary objects. `std::move` and `std::forward` are special-purpose exceptions to this rule of thumb.

Problem

Consider the following: ``` struct vec { int v[3]; vec() : v() {}; vec(int x, int y, int z) : v{x,y,z} {}; vec(const vec& that) = default; vec& operator=(const vec& that) = default; ~vec() = default; vec& operator+=(const vec& that) { v[0] += that.v[0]; v[1] += that.v[1]; v[2] += that.v[2]; return *this; } }; vec operator+(const vec& lhs, const vec& rhs) { return vec(lhs.v[0] + rhs.v[0], lhs.v[1] + rhs.v[1], lhs.v[2] + rhs.v[2]); } vec&& operator+(vec&& lhs, const vec& rhs) { return move(lhs += rhs); } vec&& operator+(const vec& lhs, vec&& rhs) { return move(rhs += lhs); } vec&& operator+(vec&& lhs, vec&& rhs) { return move(lhs += rhs); } ``` Thanks to r-value references, with these four overloads of operator+ I can minimize the number of objects created, by reusing temporaries. But I don't like the duplication of code this introduces. Can I achieve the same with less repetition?

Original source

Related problems