Reputation: 537
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?
Upvotes: 7
Views: 1622
Reputation: 385114
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.
Upvotes: 3
Reputation: 716
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 toconst
or non-const
objects, tovolatile
or non-volatile
objects, even to objects that are bothconst
andvolatile
. They can bind to virtually anything. Such unprecedentedly flexible references deserve a name of their own. I call them universal references.
Upvotes: -1
Reputation: 20579
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.
Upvotes: 5