I've got a question of similiar nature like this one posted 5 years ago:
Why are rvalues references variables not rvalue?
My major concern is why can I do this:
int&& k = 3;
k++;
but I cannot do this:
(static_cast<int&&>(3))++;
I've always interpreted rvalue references as lvalues since rvalue reference variables are lvalues. But apparently that is not the case. Can someone explain to me why the (static_cast<int&&>(3))++; results in using rvalue as lvalue ?
The confusion is probably arising from the difference between r-value and r-value reference. The former is a value-category which only applies to expressions, while the latter is a type which applies to variables (technically it would need to be an r-value reference of some type, e.g. r-value reference to int).
So the difference between the snippets you've shown is not actually related to the type of the variable, but the value-category of the expression. Postfix operator++ requires the value-category of the operand to be an l-value, regardless of the type of the operand.
In k++, the expression k is an l-value (roughly speaking, it has a name), which is its value-category. The type of the variable k is an r-value reference, but that's fine.
In (static_cast<int&&>(3))++, the expression static_cast<int&&>(3) is an r-value (it doesn't have a name), which is its value-category. Regardless of the type of static_cast<int&&> (which is int), the value-category is wrong, and so you get an error.
Note that the error message using rvalue as lvalue is referring to the value-category of the expression being used. It has nothing to do with the types of the variables.
Related
The following link provides the 4 forms of reference collapsing (if I'm correct that these are the only 4 forms): http://thbecker.net/articles/rvalue_references/section_08.html.
From the link:
A& & becomes A&
A& && becomes A&
A&& & becomes A&
A&& && becomes A&&
Although I can make an educated guess, I would like a concise explanation for the rationale behind each of these reference-collapsing rules.
A related question, if I might: Are these reference-collapsing rules utilized in C++11 internally by such STL utilities such as std::move(), std::forward(), and the like, in typical real-world use cases? (Note: I'm specifically asking whether the reference-collapsing rules are utilized in C++11, as opposed to C++03 or earlier.)
I ask this related question because I am aware of such C++11 utilities as std::remove_reference, but I do not know if the reference-related utilities such as std::remove_reference are routinely used in C++11 to avoid need for the reference-collapsing rules, or whether they are used in conjunction with the reference-collapsing rules.
The reference collapsing rules (save for A& & -> A&, which is C++98/03) exist for one reason: to allow perfect forwarding to work.
"Perfect" forwarding means to effectively forward parameters as if the user had called the function directly (minus elision, which is broken by forwarding). There are three kinds of values the user could pass: lvalues, xvalues, and prvalues, and there are three ways that the receiving location can take a value: by value, by (possibly const) lvalue reference, and by (possibly const) rvalue reference.
Consider this function:
template<class T>
void Fwd(T &&v) { Call(std::forward<T>(v)); }
By value
If Call takes its parameter by value, then a copy/move must happen into that parameter. Which one depends on what the incoming value is. If the incoming value is an lvalue, then it must copy the lvalue. If the incoming value is an rvalue (which collectively are xvalues and prvalues), then it must move from it.
If you call Fwd with an lvalue, C++'s type-deduction rules mean that T will be deduced as Type&, where Type is the type of the lvalue. Obviously if the lvalue is const, it will be deduced as const Type&. The reference collapsing rules mean that Type & && becomes Type & for v, an lvalue reference. Which is exactly what we need to call Call. Calling it with an lvalue reference will force a copy, exactly as if we had called it directly.
If you call Fwd with an rvalue (ie: a Type temporary expression or certain Type&& expressions), then T will be deduced as Type. The reference collapsing rules give us Type &&, which provokes a move/copy, which is almost exactly as if we had called it directly (minus elision).
By lvalue reference
If Call takes its value by lvalue reference, then it should only be callable when the user uses lvalue parameters. If it's a const-lvalue reference, then it can be callable by anything (lvalue, xvalue, prvalue).
If you call Fwd with an lvalue, we again get Type& as the type of v. This will bind to a non-const lvalue reference. If we call it with a const lvalue, we get const Type&, which will only bind to a const lvalue reference argument in Call.
If you call Fwd with an xvalue, we again get Type&& as the type of v. This will not allow you to call a function that takes a non-const lvalue, as an xvalue cannot bind to a non-const lvalue reference. It can bind to a const lvalue reference, so if Call used a const&, we could call Fwd with an xvalue.
If you call Fwd with a prvalue, we again get Type&&, so everything works as before. You cannot pass a temporary to a function that takes a non-const lvalue, so our forwarding function will likewise choke in the attempt to do so.
By rvalue reference
If Call takes its value by rvalue reference, then it should only be callable when the user uses xvalue or rvalue parameters.
If you call Fwd with an lvalue, we get Type&. This will not bind to an rvalue reference parameter, so a compile error results. A const Type& also won't bind to an rvalue reference parameter, so it still fails. And this is exactly what would happen if we called Call directly with an lvalue.
If you call Fwd with an xvalue, we get Type&&, which works (cv-qualification still matters of course).
The same goes for using a prvalue.
std::forward
std::forward itself uses reference collapsing rules in a similar way, so as to pass incoming rvalue references as xvalues (function return values that are Type&& are xvalues) and incoming lvalue references as lvalues (returning Type&).
The rules are actually pretty simple. Rvalue reference is a reference to some temporary value that does not persist beyond the expression that uses it - in contrast to lvalue reference which references persisting data. So if you have a reference to a persisting data, no matter what other references you combine it with, the actual referenced data is an lvalue - this covers for the first 3 rules. The 4th rule is natural as well - rvalue reference to rvalue reference is still a reference to non-persistent data, hence rvalue reference is yielded.
Yes, the C++11 utilities rely on these rules, implementation provided by your link matches the real headers: http://en.cppreference.com/w/cpp/utility/forward
And yes, the collapsing rules along with template argument deduction rule are being applied when using std::move and std::forward utilities, just like explained in your link.
The usage of type traits such as remove_reference is really depends on your needs; move and forward cover for the most casual cases.
While not the actual standard, I am relying this page on cppreference.com for this specific verbiage:
An lvalue is an expression that identifies a non-temporary object or a
non-member function.
The following expressions are lvalues:
The name of a variable or function in scope, 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.
...
My (simplified) comprehension of the quoted section above is that an lvalue:
Is a reference
Must not be a temporary object
I know that references are not objects, so point #2 must mean that in terms of a reference, it must not refer to a temporary object. However, from the expression itself, does that mean a reference to a temporary is not an lvalue? You can have references to temporary and non-temporary objects:
int myvar = 0;
int& ref_myvar = myvar; // Reference to non-temporary
class foo {};
foo const& ref_foo = foo{}; // Reference to temporary
In the above code snippet, usage of ref_foo in a separate expression later would be an lvalue or not? There is a rule for rvalue references that states that rvalue references used by name in an expression are still lvalues (because you refer to the name at that point). Does this rule also apply to lvalue references (since they also have names and using the name itself in an expression would make it an lvalue, as it does for rvalue reference variables)?
I hope I'm making some sort of sense. At least I hope the source of my confusion is evident. Some examples of how the lvalue references above would be used in an expression to prove some points relevant to my question would be a huge help as well.
I'd say that the cppreference wording is OK for a "general introduction"-level discussion or even for "most everyday uses"-level discussion. However, once you get into the fine technical details, such statements can become somewhat misleading.
The important point is that the value category ("being an lvalue") is the property of an expression, not of an object. You can have a temporary object accessed through an lvalue, and you can have a non-temporary object accessed through an rvalue.
To refer to your examples:
ref_myvar and ref_foo are both lvalues, and always will be, regardless of how you use them. In the following:
foo&& rref = foo{};
rref is, and always will be, an lvalue as well. It is a reference to an rvalue, but the reference itself has a name and so is an lvalue.
If you want to treat an lvalue as an rvalue, you use the standard-provided case operator for that:
rvalue = std::move(lvalue);
Let's analyse this code:
int someint = std::move(ref_myvar);
ref_myvar is an lvalue. std::move(ref_myvar) is an rvalue. someint is an lvalue.
I don't think there is a concise way to define an lvalue without going full standardese, but name (or absence thereof) plays an important part in most definitions. I'll try my hand at such a definition; these are lvalues:
An expression which is a name, except for enumerators and member functions.
An expression of type "lvalue reference to something."
The result of dereferencing a pointer.
Notice that ref_myvar, ref_foo, and rref are all lvalues because they have a name. std::move(ref_myvar) doesn't have a name, and so it's an rvalue.
I have used std::move and std::forward in C++. My question is: how are these functions actually implemented by the standard library?
If an lvalue is something you can get the address of, and an rvalue is exclusively not an lvalue, how can you actually implement these references?
Do these new facilities allow for something like:
auto x = &(3);
or something like that? Can you get a reference to an rvalue that isn't just a std::move/forward returned lvalue?
Hopefully these questions make sense. I couldn't find good information on Google, just tutorials on perfect forwarding, etc.
How is it possible to get a reference to an rvalue?
Conceptually, an rvalue expression creates a temporary object, or sometimes denotes an existing object. That can be bound to a reference like any other object; but, to avoid confusion, the language only allows that for rvalue and const lvalue references.
I have used std::move and std::forward in C++. My issue is how this is actually implemented by the compiler?
move simply returns an rvalue reference to its argument, equivalent to
static_cast<typename remove_reference<T>::type&&>(t)
The result of the function call is an rvalue (specifically, an xvalue), so it can be bound to an rvalue reference where the function argument couldn't. This allows you to explicitly move from an lvalue, using move to convert it to an rvalue, while not allowing you to accidentally move from it.
forward is similar, but overloaded to return an rvalue reference to an rvalue or rvalue reference, and an lvalue reference to anything else.
If an l-value is something you can get the address of
That's more or less correct. The official definition is that the expression "designates a function or an object", and those are things that have addresses.
and an r-value is exclusively not an l-value
Not really. Simplifying slightly, an expression is either a lvalue or an rvalue, but can be converted from one to the other. An lvalue can be implicitly converted to an rvalue; converting the other way can be done with a cast, as move does.
how can you actually implement these references?
Just like any other reference - as an alias for, or a pointer to, the object it's bound to. The only difference is which kinds of expression can be used to denote (and possibly create) the object that's bound to the reference.
Do these new facilities allow for something like auto x = &(3);
That attempts to take the address of an rvalue directly, which isn't allowed. Since the question is about references, not pointers, the following are allowed, binding a reference to a temporary object (whose lifetime is extended to match the reference):
auto && rvalue = 3;
auto const & const_lvalue = 3;
while it's not allowed to bind it to a non-const lvalue reference
auto & lvalue = 3; // ERROR
I cannot call a function: void foo(string* bar) like this: foo(&string("Hello World!")) or I get an error:
error: taking address of temporary
I also cannot call a function: void foo(string& bar) like this: foo(string("Hello World!")) or I get an error:
error: invalid initialization of non-const reference of type 'std::string& {aka std::basic_string&}' from an rvalue of type 'std::string {aka std::basic_string}'
What C++11 has provided me the ability to do is to make an rvalue reference, so I can call a function: void foo(string&& bar) like this: foo(string("Hello World!"));
Furthermore, internally to foo I can get the address of the object passed in by an rvalue reference:
void foo(string&& bar){
string* temp = &bar;
cout << *temp << " #:" << temp << endl;
}
It seems like the OP has a really good grip on rvalues. But this explanation of them was helpful to me, and may be to others. It goes into a bit of detail about why C++03 allowed constant references to rvalues, versus C++11's rvalue references.
Basically, compiler magic. The Standard describes the rules, the compiler maker just has to figure out how to implement the rules.
In practice, references are either optimized out or implemented as pointer on CPU level.
std::move isn't really special in that sense. It has a lvalue reference as input, and an rvalue reference as output. The compiler just has to apply the rvalue reference rules to the input.
Similarly, the goal of std::forward<T> is just to tell the compiler to apply a different set of rules to the argument, rules which happen to be defined so that perfect forwarding works. The function itself does nothing.
typedef void(&&RF)(void* p);
RF rf()
{
return f;
}
int ay[10] = { 0 };
typedef int(&&RA)[10];
RA ra()
{
return ay; // error
}
cout << is_lvalue_reference<decltype(rf())>::value << endl; // 1
The C++ reference says "rvalue references to functions are treated as lvalues whether
named or not".
But I can not understand what the considerations for this are? I guess that perhaps the name of function is always a lvalue. So it must keep its attribute of an lvalue and ensure passing the function name to anywhere it can be invoked, like rf()(NULL). Then the array name came unbidden to my mind. I think it is always a lvalue too, so I wrote the code above to test this and got a error.
Who can point out the real reason behind all of this?
In N3055 the issue of rvalue references to functions is briefly discussed:
In addition, rvalue references (like traditional lvalue references) can be bound to functions. Treating an
rvalue reference return value as an rvalue, however, introduces the novel concept of a function rvalue
into the language. There was previously no such idea – a function lvalue used in an rvalue context
becomes a pointer-to-function rvalue, not a function rvalue – so the current draft Standard does not
describe how such rvalues are to be treated. In particular, function calls and conversions to function
pointers are specified in terms of function lvalues, so most plausible uses of rvalue references to
functions are undefined in the current wording.
Functions don't have lifetime or storage duration, so the lvalue/rvalue distinction doesn't make sense for them. On the other hand, if you allow function rvalues to exist, you have to resolve the issues discussed in the quoted paragraph. In light of this, forcing all function values to be lvalues seems to me to have been a reasonable solution.
Another solution, I suppose, would have been to ban function rvalues altogether, so any attempt to create an rvalue reference to function type would result in an ill-formed program. I don't know whether this approach was considered, but my guess is that it would cause inconveniences with generic programming.
On the other hand, for any object type, including array types, there is a meaningful distinction between lvalues and rvalues. So the language forbids you from binding an rvalue reference to object type to an lvalue of object type. I'm not sure why you're surprised that your code doesn't compile.
In the last few days I've been trying to grasp an apparently trivial principle behind lvalue/rvalue references. Let us define a new rvalue reference:
int&& x = 12;
x is therefore an lvalue expression of type int&&. Since x is a lvalue, it can be bound to a lvalue reference of the same type, i.e., a lvalue reference of type int&&. Such a lvalue reference would be defined as:
int&& & ref_x = x; // non-working code, just for the sake of explanation
Of course, it is not possible to explicitly define a reference to a reference, and the correct way to perform the binding is as follows:
int& ref_x = x;
C++ Primer reports the following about using references as initializers:
when we use a reference as an initializer, we are really using the
object to which the reference is bound
On the other hand, the lvalue reference must match the type of the lvalue expression. What am I missing? Is reference collapsing involved in this case?
Thanks.
No, x (as an expression) is an expression of type int. The type of the value of an expression is never a reference. In fact, x is also an lvalue, since it is a named thing.
Also, there are no references to references, for the same reason: References bind to values, and values are never references.
If you're ever confused, just keep telling yourself: The value of an expression is always an object type. Whether the value category of an expression is l or r only determines what sort of things the value can bind to; it has no effect on its type.