Can a lvalue-ref-qualified function be used directly in a rvalue-ref-qualified function?

Viewed 78

I have been writing the following code to support function calls on rvalues without having to std::move explicitly on the return value.

struct X {
    X& do_something() & {
        // some code

        return *this;
    }

    X&& do_something() && {
        // some code

        return std::move(*this);
    }};

But this results in having to repeat the code inside the function. Preferably, I would do something like

struct X {
    X& do_something() & {
        // some code

        return *this;
    }

    X&& do_something() && {
        return std::move(do_something());
    }};

Is this a valid transformation? Why or why not?

Also, I can't help but feel that there's some knowledge gap w.r.t ref-qualifiers. Is there a general way (or a set of rules) of figuring out if code like this is valid or not?

1 Answers

Is this a valid transformation?

Yes. Inside a member function *this is always an lvalue. Even if the function is rvalue reference qualified. It's the same as

void foo(bar& b) { /* do things */ }

void foo(bar&& b) {
  // b is an lvalue inside the function
  foo(b); // calls the first overload
}

So you may use an lvalue ref qualified function to share an implementation.

And using std::move on the result is also no problem. The first overload can only return an lvalue reference, because as far as it knows, it was called on an lvalue. Meanwhile the second overload has an extra bit of information, it knows it was originally invoked on an rvalue. Therefore, it does an extra cast, based on the additional information.

std::move is just a named cast that turns lvalues into rvalues. Its purpose is to signal the designated object can be treated as though it's about to expire. Since you are doing this cast inside a context where you know this to be true (the member is originally called on an object that binds to an rvalue reference), it should not pose a problem.

Related