Reputation: 306
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:
fun()
an rvalue?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?
Upvotes: 4
Views: 1174
Reputation: 172924
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
Upvotes: 3
Reputation: 13040
The key is that the value category of an expression does not only depend on its type, e.g.
int&& a = 1;
int&& fun();
// int&& ri = a; // ill-formed, the expression a is of type int&&, but is an lvalue
int&& ri = fun(); // ok, the expression fun() is of type int&&, and is also an rvalue
In addition, as rustyx pointed out in his answer, the function definition
int && fun(){
return 1;
}
would probably result in undefined behavior, because the temporary object will be destroyed immediately after the execution of the return statement.
Upvotes: 1
Reputation: 15229
Non-const references cannot bind to rvalues, it's as simple as that.
int & a=fun();
does not work because a
is a non-const reference and fun()
is an rvalue expression.
In the second case, fun()
returns a non-const lvalue reference, which can bind to another non-const reference, of course.
decltype(fun()) b=1;
works because decltype(fun())
is int &&
and can thus bind to the integer literal 1
.
In example 1, is
fun()
an rvalue?
Yes.
In example 2, is
fun()
an rvalue reference?
No, it's an lvalue reference.
Example 3 tells us that
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'tfun()
from example 1 be bound to an lvalue reference?
Because the function fun
returns an rvalue reference, but fun()
itself is an rvalue expression. fun()
is an rvalue.
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?
An rvalue reference is an lvalue.
If the cause is that
fun()
is just an expression, which exists temporarily and will die right away, why is thefun()
in example 2 not 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?
Because in example 2, fun()
is an lvalue. From N4296, §3.10/1.1:
[...] the result of calling a function whose return type is an lvalue reference is an lvalue.
Regarding the warning you get for example 2, you should show the exact message. It's probably just because you return a reference to a local variable, though. Local variables have limited lifetime, so referencing them beyond their lifetime is undefined behavior, hence the warning.
Upvotes: 1
Reputation: 5175
I think you are mixing rvalue
and rvalue reference
. In your first example
int && fun(){
// 1 is an rvalue so it can be bound to an rvalue reference
// this will produce undefined behavior though because you
// a returning a dangling reference to an temporary that will
// go out of scope at the end of this function
return 1;
}
int main(){
// you are trying to take a reference to a temporary object.
// this is (deliberately) not valid
int & a=fun();
// One legal way of doing this is by declaring your reference const:
const int& b = fun();
// because this extends the lifetime of the temporary object returned
// by fun() to match the lifetime of the reference.
}
In your second example:
int & fun(){
// you have allocated an new int in the free store so the
// lifetime of this int is until the main exits. The return
// type here is an lvalue that can be safely bound to an
// lvalue reference
return *(new int);
}
int main(){
// binding lvalue reference to lvalue this is ok
int & a=fun();
}
In your third example
int && fun(){
// 1 is an rvalue and can be bound to an rvalue reference
return 1;
}
int main(){
// decltype(fun()) is equal to int&& so it's ok to bind
// an rvalue reference to an rvalue
decltype(fun()) b=1;
}
Upvotes: 0
Reputation: 85361
First of all, this code exhibits undefined behavior:
int && fun(){
return 1;
}
Here you're returning a dangling reference to 1
, which goes out of scope.
How does a rvalue reference become an rvalue?
In order to understand this it's good to view references not as another syntax for pointers, but as another name for some already existing object.
Then it's good to go over reference initialization rules:
The first reference initialization rule states that a reference may be initialized ("bound") to a reference-compatible value. That means
int&
can bind to int&
int&&
can bind to int&&
const int&
can bind to int&
In this case the actual referred-to value of the right-hand side is not retrieved, but is directly bound to the new reference.
Note that int&
is not compatible with int&&
, these are distinct types.
The second reference initialization rule states that a const
lvalue reference (const int&
) and an rvalue reference (int&&
) may bind to:
In case of the latter the reference binds to the result of the expression. In the case of const int& x = fun()
, the result of calling fun()
will first be "materialized" (retrieved), and then its value will be bound to the reference.
But for that to happen, the lvalue reference must be const
. That's why the error states that a non-const
int&
cannot bind to int
, because int
is the result of evaluating fun()
.
Upvotes: 1