I am trying to figure out the meaning of the following snippet:
int main() {
int&& a = 2;
int& b = a; // (*)
}
I know a is an lvalue expression of type "rvalue reference to int", and b is a general variable with type "lvalue reference to int". However, the initialization (*) of b seems weird. Since the type of a is not an int, it cannot match the type that b refers to. Can anyone explain this result? Is there any implicit conversion happening during the initialization (*)? Or is there any concept or keyword that I missed?
The code can be compiled and run successfully in here.
Any reference acts as if it's the referred object. Thus when you bind a reference b to another reference a, you actually bind it to the object a refers to.
One tricky part is that rvalue reference is an lvalue itself and C++ allows you to bind only lvalue reference to lvalues, thus you won't be able to bind another rvalue reference here:
int&& a = 2;
int&& b = a; // error
Another notable side-effect, is that a in your sample extends the lifetime of the temporary to the end of the given scope (to be precise to the end of a reference lifetime)
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;
}
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.
We can't bind non-const lvalue reference to an rvalue, but it can be bound to the const one. We can't bind rvalue reference to an lvalue also. Actually the Standard say so:
8.5.3/5.2:
the reference shall be an lvalue reference to a non-volatile const
type (i.e., cv1 shall be const), or the reference shall be an rvalue reference.
But is there a better explanation for the things than "The Standard say so"?
Because it doesn't make semantic sense.
You can't bind a non-const lvalue reference to an rvalue, because what does it mean to modify an rvalue? By definition nothing else will see the result, so this doesn't make sense.
int& i = 3;
//should change referenced location, but we aren't referencing a memory location
i = 5;
You can't bind an rvalue reference to an lvalue because rvalue references exist to facilitate destructive optimizations on their referencee. You wouldn't want your objects to be arbitrarily moved out from under you, so the standard doesn't allow it.
void process_string (std::string&&);
std::string foo = "foo";
//foo could be made garbage without us knowing about it
process_string (foo);
//this is fine
process_string (std::move(foo));
Think to some real cases:
#include <vector>
void f1( int& i ){i = 1;}
void f2( const int& i ){i = 1;}
void f3( std::vector<int>&& v ){auto v_move{v};}
int main()
{
f1(3); // error: how can you set the value of "3" to "1"?
f2(3); // ok, the compiler extend the life of the rvalue "into" f2
std::vector<int> v{10};
f3(v); // error: an innocent looking call to f3 would let your v very different from what you would imagine
f3(std::vector<int>{10}); // ok, nobody cares if the rvalue passed as an argument get modified
}
While I was reading http://thbecker.net/articles/rvalue_references/section_01.html, I got following snippiest.
// lvalues:
//
int i = 42;
i = 43; // ok, i is an lvalue
int& foo();
foo() = 42; // ok, foo() is an lvalue
int* p1 = &foo(); // ok, foo() is an lvalue
// rvalues:
//
int foobar();
int j = 0;
j = foobar(); // ok, foobar() is an rvalue
int* p2 = &foobar(); // error, cannot take the address of an rvalue
j = 42; // ok, 42 is an rvalue
Why int* p2 = &foobar(); is error statement, while int* p1 = &foo(); is not an error. How later one is lvalue while first one is rvalue?
Suppose we have the example code shown below in C. Will it compile? How do the concepts of lvalues and rvalues work in this problem?
#define X 8
int main(void)
{
++X; // will this line compile?
return 0;
}
The concept of lvalues and rvalues must be explained a bit in order to really understand the code above, and the problem being asked. Before we proceed, you should note that the definition of lvalues and rvalues presented here is not exact as even the C Standards themselves are rather vague on the definition.
The difference between rvalues and lvalues
An object is a region of memory that can be examined, but not necessarily modified. An lvalue is an expression that refers to such an object. The term lvalue originally referred to objects that appear on the left (hence the ‘l’) hand side of an expression. That definition no longer applies since any const-qualified type is also considered to be an lvalue, but it can never appear on the left hand side of an assignment statement because it can not be modified. So, the term "modifiable lvalue" was created to refer to an lvalue that can be modified, and a const-qualified type does not fall into this category.
An rvalue is any expression that has a value, but cannot have a value assigned to it. One could also say that an rvalue is any expression that is not an lvalue . An example of an rvalue would be a literal constant – something like ’8′, or ’3.14′. So, clearly the value ’8′ in the code above is an rvalue.
Using our understanding of lvalues and rvalues to answer the question
Now let’s try to solve the problem. Strictly speaking, the operand of the prefix (or postfix) increment operator must be a modifiable lvalue. So, what is the operand of the prefix increment operator in our code above?
Since X is a macro, the statement above will expand to “++8″ after the preprocessor is run. This means “8″ is the operand of the prefix increment operator. And, because 8 is an rvalue it can not be used as an argument to “++”. This, in turn, means that the code above will not compile.
So we have two functions:
int& foo();
int foobar();
foo is a function returning lvalue-reference to int
foobar is a function returning int
The function call expressions:
foobar()
foo()
both have type int (references are removed from expressions, so foo() has type int and not lvalue-reference to int). The two expressions have different value categories:
foobar() is a prvalue (a function call to a function returning a non-reference is a prvalue)
foo() is an lvalue (a function call to a function returning an lvalue-reference is an lvalue)
You can't take the address of an rvalue (a prvalue is a kind of rvalue), so &foobar() is not allowed.
You can take the address of an lvalue so &foo() is allowed.