Overloading on R-value references and code duplication

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.

  1. 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.

  2. 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.

  3. 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.

Leave a Comment