Named r-value reference binding vs assignment - c++

A named rvalue reference in an lvalue, but it is an lvalue which can only bind to an rvalue, so:
int&& x = 5; // Ok because 5 is an rvalue
int&& y = x; // Error: You cannot bind an lvalue to an rvalue reference
Ok so then why is it that I can assign after binding the reference y to an rvalue?
int&& x = 5;
int&& y = 8;
y = x; // Ok! This compiles!
x and y are both named rvalue references, so both are lvalues. Why can I bind an lvalue to an rvalue reference here, and why can I do it here but not in one line as shown in the first code snippet (int&& y = x;)?

x and y are lvalues whose types are rvalue references. Those references are being bound to temporary ints, whose lifetimes are extended to match the lifetimes of the references.
The expression y = x does not rebind y's reference. That is not possible, a reference cannot be rebound. Instead, it reads the value of the int referred by x and then assigns that value to the int referred by y.
A reference is a reference, period. The type of reference dictates whether it refers to an existing lvalue variable or to a temporary rvalue variable. You can't have a reference to a reference, that is not a thing in C++, which is why this code:
int&& x = 5;
int&& y = x;
doesn't compile.
But, this code:
int&& x = 5;
int&& y = std::move(x);
y = 7;
does compile, and works as you are expected (ie, assigning to y changes the value of the int that x refers to).
Online Demo

In the second example you are not binding anything, that's just assignment, operator=.

Related

What's the difference between an ordinary rvalue reference and one returned by std::forward?

I can't do this:
int &&q = 7;
int &&r = q;
//Error Message:
//cannot convert from 'int' to 'int &&'
//You cannot bind an lvalue to an rvalue reference
If I understand correctly, when initializing an rvalue reference, there's a temporary variable got initialized too. So int &&q = 7; can be considered as:
int temp = 7;
int &&q = temp;
And when using a reference on the right side, I am actually using the referee. So int &&r = q; can be considered as:
int &&r = temp; //bind an lvalue to an rvalue reference, cause error, understandable
So above is how I understand the compiler error occurs.
Why adding std::forward can solve that?
int &&q = 7;
int &&r = std::forward<int>(q);
I know the std::forward always returns an rvalue reference, how is the reference returned by std::forward different from int&&q?
how is the reference returned by std::forward different from int&&q ?
Their value categories are different. And note that types and value categories are different things.
q is a named variable, it's qualified as lvalue, so it can't be bound to rvalue reference.
(emphasis mine)
the name of a variable, a function, a template parameter object (since C++20), or a data member, regardless of type, such as std::cin or std::endl. Even if the variable's type is rvalue reference, the expression consisting of its name is an lvalue expression;
While rvalue reference returned from function is qualified as xvalue, which belongs to rvalue.
a function call or an overloaded operator expression, whose return type is rvalue reference to object, such as std::move(x);
The difference between the expressions q and std::forward<int>(q) is that the former is an lvalue, while the latter is an rvalue (of fundamental category xvalue).
I've addressed similar concerns in this answer: the point is that q as an expression is an lvalue, because it has a name. std::forward<int>(q) (or the equivalent std::move(q)) are expressions which don't have names, and since they return (unnamed) rvalue references, they are xvalues, which is a subcategory of rvalue and can thus bind to an rvalue reference.

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:
In example 1, is fun() an rvalue?
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?
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
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:
xvalue or prvalue
as the last resort to anything else
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().
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't fun() 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 the fun() 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.
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.
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;
}

Difference between lvalue reference and rvalue reference

I have been studying addition of rvalue reference in C++11. It wasn't straightforward but I feel like I am finally starting to get a grasp of it. However there is one particular instance where I am confused. Specifically I don't get what is the meaning of 'b' in this example:
int a = 27;
int&& b = 27;
EDIT: I know that int a = 27 is an lvalue not lvalue reference.
Also what I looking for is not what int&& b = 27 is but what is it meaning intuitively. I am confused because I thought that rvalues where not addressable, but here we have a reference to rvalue which means we can adress it. So how come it is still an rvalue?
int a = 27; is a statement, and is neither an lvalue nor an rvalue. It defines the name a, which can be used as an lvalue expression, of type int.
int&& b = 27; is also a statement and the name b is used as an lvalue expression of type int&&, and permits a conversion to an xvalue expression of type int
but here we have a reference to rvalue which means we can adress it. So how come it is still an rvalue?
Given int&& b = 27;, a temporary int is constructed from 27 and then b binds to the temporary. (And the lifetime of the temporary is extended to the lifetime of b.) After that if you get the address like &b you'll get the address of the temporary.
BTW b is an lvalue itself.
Temporary objects are created when a prvalue is materialized so that it can be used as a glvalue, which occurs (since C++17) in the following situations:
binding a reference to a prvalue
BTW: It's not special for rvalue reference; same thing happens for const int& b = 27;.
B is rvalue reference and you can assign only rvalue to it.

assigning Rvalue reference to Lvalue reference

int&& rv = 10;
int& lv = rv; //no error
How is this possible?
Is this related to "reference collapsing rule"?
int&& rv = 10;
int& lv = rv; //no error
First of all, a named object is never an rvalue. Second, since rv is named object, it is not a rvalue, even though it binds to rvalue. Since rv is lvalue, it can bind to lvalue without any problem.
Note that rvalue-ness is a property of an expression, not a variable. In the above example, an rvalue is created out of 10 and binds to rv, which as I said, is lvalue.

Is it necessary to have a temporary or a literal to have an rvalue?

This question asks if all temporaries are rvalue.
The answer is no, because if we consider this expression:
const int &ri = 2 + 3;
then, the very same temporary (2 + 3), which is an rvalue here, can be used
as an lvalue in a subsequent expression:
const int *pi = &ri;
so this temporary is not (only) an rvalue.
The logic statement temporary ==> rvalue is then false.
However, we cannot write
const int &ri = &(2 + 3); // illegal, 2 + 3 -> temporary -> rvalue
or
int *i = &4; // illegal, 4 is an rvalue (literal)
or
int foo();
int *i = &foo(); // illegal, foo() -> temporary -> rvalue
Thus my question is, can we generate an rvalue in a certain expression without
having a temporary or a literal? Is rvalue ==> (temporary or literal) true?
Expressions that yield temporary objects are r-values. There's a special rule which allows const-references and r-value references to bind to r-values, and this extends the lifetime of the temporary object to that of the reference (see 12.2(5)), but that does not make the temporary-object expression any less of an r-value.
However, once bound to a reference, the reference variable itself has a name, and thus the reference expression is an l-lvalue.
Don't confuse expressions, variables and objects.
The rvalue and lvalue attributes apply to expressions, not to objects. An expression can be either an lvalue or an rvalue. Oversimplifying a expression that yields a value is an rvalue-expression and an expression that yields an object is an lvalue-expression. The lvalue to rvalue conversion is the act of reading the value out of an object.
A expression that yields a temporary and a literal are both rvalue-expressions they represent a value not an actual object.
In your example:
const int &ri = 2 + 3;
const int *pi = &ri;
The expression 2+3 is an rvalue-expression used to initialize a constant reference. That, according to the language implies the extension of the lifetime of the temporary beyond the current expression and until the reference goes out of scope. After that, in the second expression, the subexpression ri is an lvalue-expression that refers to the temporary object, whose lifetime has been extended.
Note that there are other ways of creating rvalue expressions with temporaries, for example calling a member that yields a reference:
struct test {
test& thisTest() { return *this; }
};
test foo();
... foo().thisTest()
The subexpression foo() is an rvalue-expression, but the expression foo().thisTest() is an lvalue-expression. Both of them refer to a temporary object that will disappear at the end of the full expression.