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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With