Rvalue Returned from a Function is a Right Value

In C++, an rvalue reference (denoted by T&&) is a reference type that can bind to temporary objects (rvalues). When a function returns an rvalue reference, the object it refers to is typically a temporary or an object that is about to go out of scope. In the caller's context, this returned rvalue reference is considered an rvalue because it refers to a temporary object or an object that is no longer tied to a named variable.

For example:

#include <iostream>
#include <utility>

int&& returnRvalueRef() {
    int x = 42;
    return std::move(x); // Returns an rvalue reference to x
}

int main() {
    int&& r = returnRvalueRef(); // Binds to the rvalue reference, but r is now an lvalue
    std::cout << r << std::endl; // Prints 42 (undefined behavior in practice, see note)
}

Key Points:

  1. Rvalue Reference Return Type: When a function returns T&&, the returned value is treated as an rvalue in expressions outside the function. This allows it to be used in contexts where rvalues are expected, such as move semantics or binding to another rvalue reference.

  2. Temporary Nature: The object referred to by the returned rvalue reference is typically a temporary. If the function returns an rvalue reference to a local variable (as in the example above), it leads to undefined behavior because the local variable x is destroyed when the function returns, leaving the rvalue reference dangling.

  3. Binding to an lvalue: If you bind the returned rvalue reference to a named variable (e.g., int&& r = returnRvalueRef();), that named variable becomes an lvalue in the caller's scope, even though it was initialized with an rvalue. This is because any named variable is inherently an lvalue.

  4. Correct Usage: Returning an rvalue reference is often used in scenarios like move semantics or perfect forwarding, where the caller expects to take ownership of a temporary or movable object. For example:

    std::string&& createString() {
        std::string s = "example";
        return std::move(s); // Returns rvalue reference
    }
    
    int main() {
        std::string s = createString(); // Move-constructed from the temporary
    }

    Here, the rvalue reference enables move semantics, but the actual object is a temporary that is safely moved into s.

Caveat:

Returning an rvalue reference to a local variable (as in the first example) is dangerous and usually leads to undefined behavior because the local variable is destroyed when the function exits. To avoid this, you should ensure the rvalue reference refers to an object that outlives the function call (e.g., a dynamically allocated object or a temporary explicitly created for the return).

It also disallow the RVO (Return Value Optimization) by the compiler. Check Effective Mordern C+++ - Item 25: Use std::move on rvalue references, std::forward on universal references.

Conclusion:

An rvalue reference returned from a function is treated as an rvalue in the caller's context, allowing it to participate in move semantics or bind to another rvalue reference. However, care must be taken to avoid returning references to local variables to prevent undefined behavior.

Last updated

Was this helpful?