Correct way to return an rvalue reference to this

c++, c++11, c++14, rvalue

Solution

It is NOT ok to return `std::move(*this)` on a member overloaded for the `&&` qualifier. The problem here is not with the `std::move(*this)` (which other answers correctly show that it is ok) but with the return type. The problem is very subtle and it was almost done by the c++11 standardization committee. It is explained by Stephan T. Lavavej in his presentation Don’t Help the Compiler during `Going Native 2013`. His example can be found at around minute 42 in the linked video. His example is slightly different and doesn’t involve `*this` and uses overload by parameter reference type rather than by method ref qualifiers but the principle is still the same.

So what is wrong with the code?

Short introduction: a reference bound to a temporary object prolongs the lifetime of the temporary object for the lifetime of the reference. That is what makes code like this be ok:

void foo(std::string const & s) {
  //
}

foo("Temporary std::string object constructed from this char * C-string");

The important part here is that this property is not transitive, meaning that for the reference to prolong the lifetime of the temporary object, it must bind directly to the temporary object, and not to a reference to it.

Returning to my example:

For completness let’s add a function that takes only a const lvalue reference to `Avenger` (no rvalue reference overload):

void doInjustice(Avenger const &) {};

the next two calls result in UB if referencing the parameter inside the functions:

doInjustice(Avenger{} << 24); // calls `void doInustice(Avenger const &) {};`
doJustice(Avenger{} << 24); // calls `void doJustice(Avenger &&) {};` 

The temporary objects constructed at parameter evaluation are destroyed as soon as the function are called for the reasons exposed above and the parameters are dangling references. Referencing them inside the functions will result in UB.

The correct way is to return by value:

class Avenger {
  public:
    Avenger& operator<<(int) & {
      return *this;
    }
    Avenger operator<<(int) && {
      return std::move(*this);
    }
};

A copy is still eluded with the move semantics, and the return is a temporary, meaning that it will call the correct overload, but we avoid this subtle but nasty silent bug.

The example of `Stephan T. Lavavej`: Don’t Help the Compiler (42m–45m)

string&& join(string&& rv, const char * ptr) {
  return move(rv.append(", ").append(ptr));
}
string meow() { return "meow"; }

const string& r = join(meow(), "purr");
// r refers to a destroyed temporary!

//Fix:
string join(string&& rv, const char * ptr) {
  return move(rv.append(", ").append(ptr));
}

Posts on SO explaining the prolonging of life of temporary objects through references:

- Does a const reference prolong the life of a temporary?

- Initializing a reference

Problem

The code below results in Undefined Behaviour. Be sure to read ALL the answers for completeness. When chaining an object via the `operator<<` I want to preserve the lvalue-ness / rvalue-ness of the object: ``` class Avenger { public: Avenger& operator<<(int) & { return *this; } Avenger&& operator<<(int) && { return *this; // compiler error cannot bind lvalue to rvalue return std::move(*this); } }; void doJustice(const Avenger &) {}; void doJustice(Avenger &&) {}; int main() { Avenger a; doJustice(a << 24); // parameter should be Avenger& doJustice(Avenger{} << 24); // parameter should be Avenger&& return 0; } ``` I cannot simply return `*this` which implies that the type of `*this` of an `rvalue` object is still an `lvalue reference`. I would have expected to be an `rvalue reference`. - Is it correct / recommended to return `std::move(*this)` on an member overloaded for the `&&` qualifier, or should other method be used? I know that `std::move` is just a cast, so I think it’s ok, I just want to double check. - What is the reason/explanation that `*this` of an `rvalue` is an `lvalue reference` and not an `rvalue reference`? - I remember seeing in C++14 something about move semantics of `*this`. Is that related to this? Will any of the above change in C++14?

Original source

Related problems