Search code examples
c++return-typervalue-referencervaluelvalue

Why rvalue reference as return type can't be initialization of non-const reference?


I read this question and I know that an rvalue referenec is an lvalue.

However, for this code, example 1,

int &&fun() {
    return 1;
}

int main() {
    int &a = fun();
}

When I compile it:

 error: invalid initialization of non-const reference of type 'int&' from an rvalue of type 'int'

So the C++ compiler tells me the return type of fun is an rvalue.

How does a rvalue reference become an rvalue?

I think the compiler should treat lvalue reference and rvalue reference by the same way, but this code, example 2,

int & fun(){
    int b;
    return b;
}
int main(){
    int & a=fun();
}

can be compiled (nevertheless, I get a warning).

I think maybe the return type of fun has changed at some point.

Trying to compile example 3:

int &&fun() {
    return 1;
}

int main() {
    decltype(fun()) b = 1;
}

it compiles successfully. So I can say the return type of fun is really an rvalue reference.

So, why does an rvalue reference become an rvalue?

Here is example 4:

int &&a = 1;
int &b = a;

It compiles and tells us an rvalue reference can be bound to an lvalue reference.

Now, what about those two questions:

  1. In example 1, is fun() an rvalue?
  2. In example 1, is fun() an rvalue reference?

Example 3 tells us fun() is an rvalue reference,and example 4 tells us an rvalue reference can be bound to an lvalue reference (both const and non-const). Then why can't the fun() from example 1 be bound to an lvalue reference?

Example 4 also indicates that an rvalue reference is an lvalue, but the compilation error in example 1 tells us that fun() there, which is proved to be an rvalue reference in example 3, is an rvalue. So, is an rvalue reference lvalue or rvalue?

If the cause is that fun() is just an expression, which exists temporarily and will die right away, why is fun() from example 2 not be regarded an rvalue while it is also just an expression without a name? What difference is there between a function expression of a function returning an lvalue reference and an rvalue reference?


Solution

  • I know that an rvalue reference is an lvalue.

    You're talking about two different things: type and value category. e.g.

    int&& ri = 0; // ri's type is rvalue reference (to int)
                  // ri's value category is lvalue; it's a named variable.
    

    Given your 1st sample, what fun() returns is an xvalue, which belongs to rvalues.

    The following expressions are xvalue expressions:

    • a function call or an overloaded operator expression, whose return type is rvalue reference to object, such as std::move(x);

    then,

    int &a = fun(); // fails; lvalue-reference can't bind to rvalue
    

    In the 2nd sample, what fun() returns is an lvalue,

    The following expressions are lvalue expressions:

    • a function call or an overloaded operator expression, whose return type is lvalue reference, such as std::getline(std::cin, str), std::cout << 1, str1 = str2, or ++it;

    then

    int & a=fun(); // fine; lvalue-reference could bind to lvalue
    

    In the 3rd sample,

    decltype(fun()) b = 1; // the return type of fun() is rvalue-reference;
                           // this has nothing to do with the value category of its return value
                           // b's type is rvalue-reference too, btw its value category is lvalue
    

    In the 4th sample,

    int &&a = 1; // fine; rvalue-reference could bind to rvalue
                 // a's type is rvalue-reference, its value category is lvalue
    int &b = a;  // fine; lvalue-reference could bind to lvalue
                 // b's type is lvalue-reference, its value category is lvalue