I know that a named reference is an lvalue:
int x = 1;
int& ref1 = x;
int&& ref2 = std::move(x);
I've read the explanation — that is because we can take the address of those ref1 and ref2.
But when we take the address of a reference we actually take the address of the referenced object, don't we? So this explanation doesn't seem to be correct.
So why a named reference is an lvalue?
Per [expr.prim.id.unqual] (8.1.4.1 Unqualified names):
[...] The expression is an lvalue if the entity is a function,
variable, or data member and a prvalue otherwise; it is a bit-field if
the identifier designates a bit-field ([dcl.struct.bind]).
Per [basic]/6:
A variable is introduced by the declaration of a reference other
than a non-static data member or of an object. The variable's name, if
any, denotes the reference or object.
The declaration
int&& ref2 = std::move(x);
is a "declaration of a reference other than a non-static data member." Therefore, the entity denoted by ref2 is a variable. So the expression ref2 is an lvalue.
That explanation is just a simplification. lvalues aren't defined by being "something you can take the address of", but by a specific set of rules about the value category of expressions. Those rules are carefully constructed so as to result in a self-consistent language in which everything fits together reasonably neatly.
That being said, the explanation does rather fit here, if you consider that by writing ref1, you're not really naming "the reference" but the thing being referred to. That's the magic of references: you're supposed to consider them name aliases rather than entities in their own right.
There are some abstraction leaks surrounding this (particularly, member references), but that's the gist.
You ought to forget about notions like "the reference is an lvalue" and instead think about expressions. Objects have types; expressions have value categories.
Here is an explanation from Scott Meyers's book "Effective Modern C++":
In fact, T&& has two different meanings. One is rvalue reference, of course. Such references behave exactly the way you expect: they bind only to rvalues, and their primary raison d’être is to identify objects that may be moved from.
void f(Widget&& param); // rvalue reference
Widget&& var1 = Widget(); // rvalue reference
auto&& var2 = var1; // not rvalue reference
template<typename T>
void f(std::vector<T>&& param); // rvalue reference
template<typename T>
void f(T&& param); // not rvalue reference
The other meaning for T&& is either rvalue reference or lvalue reference. Such references look like rvalue references in the source code (i.e., T&&), but they can behave as if they were lvalue references (i.e., T&). Their dual nature permits them to bind to rvalues (like rvalue references) as well as lvalues (like lvalue references). Furthermore, they can bind to const or non-const objects, to volatile or non-volatile objects, even to objects that are both const and volatile. They can bind to virtually anything. Such unprecedentedly flexible references deserve a name of their own. I call them universal references.
Related
I know that a named reference is an lvalue:
int x = 1;
int& ref1 = x;
int&& ref2 = std::move(x);
I've read the explanation — that is because we can take the address of those ref1 and ref2.
But when we take the address of a reference we actually take the address of the referenced object, don't we? So this explanation doesn't seem to be correct.
So why a named reference is an lvalue?
Per [expr.prim.id.unqual] (8.1.4.1 Unqualified names):
[...] The expression is an lvalue if the entity is a function,
variable, or data member and a prvalue otherwise; it is a bit-field if
the identifier designates a bit-field ([dcl.struct.bind]).
Per [basic]/6:
A variable is introduced by the declaration of a reference other
than a non-static data member or of an object. The variable's name, if
any, denotes the reference or object.
The declaration
int&& ref2 = std::move(x);
is a "declaration of a reference other than a non-static data member." Therefore, the entity denoted by ref2 is a variable. So the expression ref2 is an lvalue.
That explanation is just a simplification. lvalues aren't defined by being "something you can take the address of", but by a specific set of rules about the value category of expressions. Those rules are carefully constructed so as to result in a self-consistent language in which everything fits together reasonably neatly.
That being said, the explanation does rather fit here, if you consider that by writing ref1, you're not really naming "the reference" but the thing being referred to. That's the magic of references: you're supposed to consider them name aliases rather than entities in their own right.
There are some abstraction leaks surrounding this (particularly, member references), but that's the gist.
You ought to forget about notions like "the reference is an lvalue" and instead think about expressions. Objects have types; expressions have value categories.
Here is an explanation from Scott Meyers's book "Effective Modern C++":
In fact, T&& has two different meanings. One is rvalue reference, of course. Such references behave exactly the way you expect: they bind only to rvalues, and their primary raison d’être is to identify objects that may be moved from.
void f(Widget&& param); // rvalue reference
Widget&& var1 = Widget(); // rvalue reference
auto&& var2 = var1; // not rvalue reference
template<typename T>
void f(std::vector<T>&& param); // rvalue reference
template<typename T>
void f(T&& param); // not rvalue reference
The other meaning for T&& is either rvalue reference or lvalue reference. Such references look like rvalue references in the source code (i.e., T&&), but they can behave as if they were lvalue references (i.e., T&). Their dual nature permits them to bind to rvalues (like rvalue references) as well as lvalues (like lvalue references). Furthermore, they can bind to const or non-const objects, to volatile or non-volatile objects, even to objects that are both const and volatile. They can bind to virtually anything. Such unprecedentedly flexible references deserve a name of their own. I call them universal references.
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.
Sec. 5.4/1 N3797 says:
The result is an lvalue if T is an lvalue reference type or an rvalue
reference to function type and an xvalue if T is an rvalue reference
to object type.
What does it mean? I know what means lvalue expression. I've been trying to find an lvalue reference type definition int the Standard, but I can't. Could you possibly provide it?
The term lvalue reference means a reference with one & in it, e.g.:
int &x = whatever;
int const &y = whatever;
In C++03, lvalue references were the only type of reference. In C++11, rvalue references were added, so the retronym lvalue reference was coined to mean non-rvalue references.
Lvalue references can bind to both lvalues and rvalues (with the restriction about non-const lvalue reference not being able to bind to a temporary object); rvalue references can only bind to rvalues.
Note: universal references may bind to lvalues or rvalues, and after binding they either behave like an lvalue reference, or like an rvalue reference.
There are 2 types of lvalue reference that are described in The C++ Programming Language 4th Edition as follow,
A non-const lvalue reference refers to an object, to which the user of the reference can write.
A const lvalue reference refers to a constant, which is immutable from the point of view of the user of the reference.
in addition,
An rvalue reference refers to a temporary object, which the user of the reference can (and typically will) modify, assuming that the object will never be used again.
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.
I see code on StackOverflow every once in a while, asking about some overload ambiguity with something involving a function like:
void foo(int&& param);
My question is: Why does this even come up? Or rather, when would you ever have "a reference to a reference"? How is that any different from a plain old reference? I've never run across this in real-world code, so I'm curious as to what kind of code would need this.
It's a rvalue reference, Bjarne describes it here.
Shameless copying ("quoting"):
The rvalue reference
An rvalue reference is a compound type
very similar to C++'s traditional
reference. To better distinguish these
two types, we refer to a traditional
C++ reference as an lvalue reference.
When the term reference is used, it
refers to both kinds of reference:
lvalue reference and rvalue reference.
An lvalue reference is formed by
placing an & after some type.
A a; A& a_ref1 = a; // an lvalue reference
An rvalue reference is formed by
placing an && after some type.
A a; A&& a_ref2 = a; // an rvalue reference
An rvalue reference behaves just like
an lvalue reference except that it can
bind to a temporary (an rvalue),
whereas you can not bind a (non const)
lvalue reference to an rvalue.
A& a_ref3 = A(); // Error!
A&& a_ref4 = A(); // Ok
It isn't a reference to a reference: such a thing does not exist.
It is an rvalue reference, a new feature added in C++11.
It's an rvalue reference. Note that the && token used for Boolean AND and rvalue references, and the & token used for bitwise AND and normal references, are different "words" as far as the language can tell.
An rvalue reference is (usually) bound to an object which may be left in an indeterminate state after the rvalue reference is finished, presumably because the object will then be destroyed.
Simply put, binding a variable to an rvalue reference is the usually the last thing you do to it.
Unlike a regular reference but like a const & reference, an rvalue reference can bind to an rvalue (an expression whose address cannot be directly taken). Like a regular reference but unlike a const & reference, an rvalue reference can be used to modify its object.