Understanding reference binding - c++

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
}

Related

C++11 rvalue reference vs const reference

This may be obvious but I think it is something difficult to me. Given this:
void test(std::string&&) { }
std::string x{"test"};
test(std::move(x)); // ok
This code calls test() with a rvalue reference as parameter so the program compiles as I expect.
Now look at this:
void other_test(const std::string&) { }
std::string x{"test"};
other_test(std::move(x)); // ok???
And here I'm tilted. Why does this version compile? The std::move returns a && type; why then I don't get an error in the second method where I use const&?
I know that
int&& s = 5;
const int& s = 5;
is valid because in both cases I provide something that has not an lvalue, it has no addresses. Are && and const& equivalent? If no, are there differences?
std::move doesn't actually move anything out of it's own. It's just a fancy name for a cast to a T&&. Calling test like this test(std::move(x)); only shows that a T&& is implicitly convertible to a const T&. The compiler sees that test only accepts const T& so it converts the T&& returned from std::move to a const T&, that's all there is to it.
In simple terms:
&& can bind to non-const rvalues (prvalues and xvalues)
const && can bind to rvalues (const and non-const)
& can bind to non-const lvalues
const & can bind to rvalues (prvalues and xvalues) and lvalues (const and non-const for each). A.k.a. to anything.
If you want a function to expressly allow const-Lvalue objects, but expressly disallow Rvalue objects, write the function signature like this:
void test(const std::string&) { }
void test(std::string&&) = delete;//Will now be considered when matching signatures
int main() {
std::string string = "test";
test(string);//OK
//test(std::move(string));//Compile Error!
//test("Test2");//Compile Error!
}
test(std::string&& a) {
something(a) //--> not moved because it has lvalue
Names of variables are lvalues. a is a name of a variable, therefore a is an lvalue expression, and therefore it will not be moved from.
It's unclear what you mean by "has". a is an expression. It is a name of a reference, and references refer to objects. Value categories pertain to expressions, not objects.
test(const std::string& a): a is const lvalue reference and like before I have lvalue and rvalue. And plus more, in this case if I called
std::move(a)
where a is a const& the move works!
If by "works" you mean that it invokes a move constructor or assignment, then no, it does not work because no move construction or assignment has happened.
When you call std::move(x), an rvalue reference to the underlying data, test, will be returned. You are allowed to pass rvalue references as const (and const only!) reference parameters because an rvalue reference is implicitly convertible to a const reference. They are arguably the same thing from the function's point of view (a read only parameter). If you removed the const-qualifier of your parameter, this code would not compile:
void other_test(std::string&) { }
std::string x{"test"};
other_test(std::move(x)); //not okay because
//the function can potentially modify the parameter.
See Bo Qian's youtube video on rvalue vs lvalue.

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;
}

r-value parameters in a function

I was wondering about a c++ behaviour when an r-value is passed among functions.
Look at this simple code:
#include <string>
void foo(std::string&& str) {
// Accept a rvalue of str
}
void bar(std::string&& str) {
// foo(str); // Does not compile. Compiler says cannot bind lvalue into rvalue.
foo(std::move(str)); // It feels like a re-casting into a r-value?
}
int main(int argc, char *argv[]) {
bar(std::string("c++_rvalue"));
return 0;
}
I know when I'm inside bar function I need to use move function in order to invoke foo function. My question now is why?
When I'm inside the bar function the variable str should already be an r-value, but the compiler acts like it is a l-value.
Can somebody quote some reference to the standard about this behaviour?
Thanks!
str is a rvalue reference, i.e. it is a reference only to rvalues. But it is still a reference, which is a lvalue. You can use str as a variable, which also implies that it is an lvalue, not a temporary rvalue.
An lvalue is, according to §3.10.1.1:
An lvalue (so called, historically, because lvalues could appear on the left-hand side of an assignment expression) designates a function or an object. [ Example: If E is an expression of pointer type, then *E is an lvalue expression referring to the object or function to which E points. As another example, the result of calling a function whose return type is an lvalue reference is an lvalue. —end example ]
And an rvalue is, according to §3.10.1.4:
An rvalue (so called, historically, because rvalues could appear on the right-hand side of an assignment
expression) is an xvalue, a temporary object (12.2) or subobject thereof, or a value that is not associated with an object.
Based on this, str is not a temporary object, and it is associated with an object (with the object called str), and so it is not an rvalue.
The example for the lvalue uses a pointer, but it is the same thing for references, and naturally for rvalue references (which are only a special type of references).
So, in your example, str is an lvalue, so you have to std::move it to call foo (which only accepts rvalues, not lvalues).
The "rvalue" in "rvalue reference" refers to the kind of value that the reference can bind to:
lvalue references can bind to lvalues
rvalue references can bind to rvalues
(+ a bit more)
That's all there's to it. Importantly, it does not refer to the value that get when you use the reference. Once you have a reference variable (any kind of reference!), the id-expression naming that variable is always an lvalue. Rvalues occur in the wild only as either temporary values, or as the values of function call expressions, or as the value of a cast expression, or as the result of decay or of this.
There's a certain analogy here with dereferencing a pointer: dereferencing a pointer is always an lvalue, no matter how that pointer was obtained: *p, *(p + 1), *f() are all lvalues. It doesn't matter how you came by the thing; once you have it, it's an lvalue.
Stepping back a bit, maybe the most interesting aspect of all this is that rvalue references are a mechanism to convert an rvalue into an lvalue. No such mechanism had existed prior to C++11 that produced mutable lvalues. While lvalue-to-rvalue conversion has been part of the language since its very beginnings, it took much longer to discover the need for rvalue-to-lvalue conversion.
My question now is why?
I'm adding another answer because I want to emphasize an answer to the "why".
Even though named rvalue references can bind to an rvalue, they are treated as lvalues when used. For example:
struct A {};
void h(const A&);
void h(A&&);
void g(const A&);
void g(A&&);
void f(A&& a)
{
g(a); // calls g(const A&)
h(a); // calls h(const A&)
}
Although an rvalue can bind to the a parameter of f(), once bound, a is now treated as an lvalue. In particular, calls to the overloaded functions g() and h() resolve to the const A& (lvalue) overloads. Treating a as an rvalue within f would lead to error prone code: First the "move version" of g() would be called, which would likely pilfer a, and then the pilfered a would be sent to the move overload of h().
Reference.

What is the difference between rvalue reference and xvalue?

I'm new to C++ and this is my first question here so bear with me please ... I have been reading about lvalue and rvalue for a while and I think I understand most of it but there is bit that still confuses me ... so my question will be specific
rvalue references are considered lvalue (this part I understand) but functions that return rvalue references are considered rvalue (or xvalue to be specific) for instance:
int x = 32;
int& Lref = x; // Lref is lvalue ... ok
int& funcA(); // calling funcA() is lvalue ... ok
int&& Rref = 32; // Rref is lvalue ... ok I got this
int&& funcB(); // calling funcB() is rvalue ... Why?
So the question is: why calling funcB() which return rvalue reference is considered rvalue ?
Thanks in advance.
To answer the titular question, "rvalue reference" is a kind of type, while "xvalue" is a kind of expression.
rvalue references are considered lvalue (this part I understand)
They are not. Rvalue references are types, types are not expressions and so cannot be "considered lvalue". What you're referring to is the fact that if an expression consists solely of the name of a variable of type T&&, then it is an lvalue expression of type T.
why calling funcB() which return rvalue reference is considered rvalue
The most straightforward answer would be "by definition". A function call expression where the function's return type is T&& is an xvalue expression of type T. As for motivation, this is exactly what makes std::move do what it does: imbue any expression with the ability to be moved from (also known as "rvalue category" - see http://en.cppreference.com/w/cpp/language/value_category ).
Generally, "object" returned by a function are created on the stack part associated to the function itself. That is it, the value returned have to be copied (or moved) to a new object and this object is an rvalue.
In your code, you did a mistake for the lref. 32 is a rvalue reference.
To be simple, lvalue reference are object that we can obtain an address. We can't get the address of a rvalue reference.
int a = 50;
int &b = a; // Ok because we can get the address of a
int &c = 50; // Error we can't get the address of 50
int &&d = 50; // It is ok
And it works as well with "object".
Take for example a code with an unique_ptr which is not copyable.
std::unique_ptr<int> foo() {
auto ptr = std::make_unique<int>(5);
return ptr;
}
auto a = foo(); // is correct, the ptr will be moved because it is a rvalue
This function must return an rvalue to be correct. (Normally, the compiler when you don't specifize if it a lvalue or rvalue will use an rvalue reference).
Maybe you could take a look at :
http://en.cppreference.com/w/cpp/language/value_category
If it is unclear, let me know

Passing an integer to a function asking for reference

Why is this code well-formed? I'm not passing a reference to the function:
void function(const int& ref) {
}
int main()
{
function(1);
}
Constant lvalue references can bind to rvalues. Rvalues, like your literal 1, don't have a persistent alias, so if you were to modifying it, you wouldn't be able to observe the effect, but if you promise not to modify it (namely by accessing it through a constant reference), you can still have perfectly sensible code, and that's why this binding is allowed.
(You can also bind rvalues to (mutable) rvalue references: void function(int &&) In that case, the rvalue reference becomes the (unique) alias of the value.)
Note also that without this rule it would be impossible to initialize variables from functions that return prvalues, or use copy-initialization at all:
struct T { T(int); };
T f();
T x = 1; // === "T x = T(1);", copy constructor wants to bind to prvalue
T x = f(); // ditto
T x((f())); // ditto
The compiler can create a temporary from the constant and temporaries are allowed to bind to const references. If the reference wasn't const, this wouldn't be allowed.