Is it correct to think of an glvalue as an prvalue with an address?
First, all value categories describe expressions, that is, instructions to compute something. Or, if you want to think of something concrete, they are fragments of the abstract syntax tree of the program. ASTs don't "have" addresses.
The expressions that happen to allow the address-of operator to be tacked on to the root of their syntax trees also happen to be classified as lvalue expressions (because their evaluation identifies an object and objects are the entities that have addresses).
Evaluations of prvalue expressions, instead, initialize objects. That "result object" itself has an address, but the expression used to initialize it, like 42
, does not: its evaluation results in a pure value that is the same exact entity whether you compute it in C++, write on paper, or pronounce it in Japanese.
So, all l-values can be r-values, but not all r-values can be l-values.
That book is about as credible as a random youtube video, but the bit this quote is misinterpreting is actually notable: it's about a set of implicit conversions called value tranformations: if you place an lvalue expression in a position where only rvalues are allowed, the compiler will insert an invisible conversion node in the AST (if type constraints are also satisfied). Just as it will do if you use an int
where a double
is expected (hopefully it's easier to see how "all ints can be doubles" is a misinterpretation)
Here's the clang ast for the expression n+1.0
where n
names an int
variable:
`-BinaryOperator 0x5637e2056340 <col:12, col:14> 'double' '+'
|-ImplicitCastExpr 0x5637e2056328 <col:12> 'double' <IntegralToFloating>
| `-ImplicitCastExpr 0x5637e2056310 <col:12> 'int' <LValueToRValue>
| `-DeclRefExpr 0x5637e20562d0 <col:12> 'int' lvalue ParmVar 0x5637e2056118 'n' 'int'
`-FloatingLiteral 0x5637e20562f0 <col:14> 'double' 1.000000e+00
The sub-expression n
becomes the DeclRefExpr node (int lvalue), but it's used where rvalues are expected, so compiler builds a larger expression (ImplicitCastExpr), that is an int rvalue. It's used where doubles are expected, so it makes an even larger ImplicitCastExpr, that is a double rvalue, and finally operator+'s requirements are satisfied and the compiler can complete the tree attaching the two double rvalues as its operands.