Why does it use the term "object" here when mentioning a prvalue? - c++

As far as I know,in c++17 the concept/semantic of prvalue is no longer temporary object,so in many circumstances the copy elision is mandated.
However, today I came cross a description of return expression
If expression is a prvalue, the object returned by the function is initialized directly by that expression. This does not involve a copy or move constructor when the types match
Why does the term object occurs here? In value category the return of function which is not a reference type belongs to prvalue,so I think maybe it is inappropriate to use the term object.
According to my understanding,the prvalues are no longer objects now,they are just values,am I right?
As a supplement,here also use the term "object".

I agree with what you say. There is a discussion page on cppreference where you can raise your concerns. A better way to phrase it might be
If expression is a prvalue, the result object of the expression is initialized directly by that expression.
As you say, objects are no longer returned or passed around by prvalues.

The quoted text is pointing out that there is an object whose value is set to the return value of the function: with C++17's guaranteed elision when returning by value, that object will be something like a variable being created by the caller, an element in a vector the caller is emplacing or push_backing too, or an object being constructed in some dynamically allocated memory the caller's orchestrated. You're confusing this with a temporary, which as you say might not be involved.
Working through it systematically, you quote cppreference saying of...
return expression;
...that...
If expression is a prvalue, the object returned by the function is initialized directly by that expression. This does not involve a copy or move constructor when the types match
The C++17 Standard says in [basic.lval]:
The result of a prvalue is the value that the expression stores into its context. [ ... ] The result object of a prvalue is the object initialized by the prvalue; a prvalue that is used to compute the value of an operand of an operator or that has type cv void has no result object. [ Note: Except when the prvalue is the operand of a decltype-specifier, a prvalue of class or array type always has a result object. For a discarded prvalue, a temporary object is materialized; ...
So, in the cppreference text, what the standard terms the "result object" is being referred to as "the object returned by the function". The language is a little bit imprecise in saying the result object is "returned" rather than "initialised", but overall it's not misleading and - for having avoided yet another bit of terminology - may well be easier for most cppreference readers to understand. I'm not actively involved with the site, but as a regular user my impression is that cppreference is trying to accurately explain the essence of the standard but simplifying language a smidge when possible.
While the underlying mechanisms are left unspecified by the Standard - and the practicalities of optimisation / ABIs dictate different implementation - to get a sense of what the Standard requires happen functionally it might help to imagine the compiler implementing code like...
My_Object function() { return { a, b, c }; }
...
... {
My_Object my_object = function(); // caller
}
...by secretly passing a memory-for-returned-object address to the function (much like the this pointer to member functions)...
void function(My_Object* p_returned_object) {
new (p_returned_object) My_Object{ a, b, c };
}
So, there is an object involved and constructed by the called function, but its whereabouts is elided - i.e. a caller-specified memory address. If the function call result is unused by the caller, a temporary is at least notionally constructed then destructed.

Related

Expression type vs. expression value category vs. object type and when is which used?

There are three somewhat different concepts in c++:
Value category of an expression
lvalue, rvalue, etc.
I'm not able to formalize this, but roughly said, it's something that can often be relatively easy to deduce in a purely grammatical way. For example with "1.0" or "n" you often don't need any sort of knowledge of the context - you simply look at the actual symbols of the expression and can deduce the expression.
There are exceptions to this however - with f(x) for example we need to know whether f is a function or a type, and we also need to know whether it returns a value/reference type.
Type of an expression
An intuitive explanation of this would be "if this expression were to be evaluated, it would have some value. What would be the type of this value ?". Because it has to do with values, there can not be any sort of reference type - the type of an expression is a non reference type.
Unlike the above, this can easily require some sort of knowledge of the context - to deduce the type of "n" you need a symbol table that stores the type of "n".
Type of an object
This seems a bit more complicated, as an object is something that really only exists in the abstract, at runtime. While it's not something directly understandable as something in source code, regardless of the philosophical difference, we can say that whatever the object is, the type of the object is induced by a declaration expression
e.g. for int& x the object that the identifier x is associated with, is of type int&
//
1, Are the descriptions above correct in the sense that they at least roughly match how these concepts are described in the standard?
2, Assuming they are, how and in what situations are these properties used?
As an example, to resolve overloading, it seems to me that only the expression properties can be used for example - the relevant property of whether the object type is a reference or not is encapsulated in the expression value category to some extent, so we don't strictly need to know whether the return object is an lvalue reference type or not - the case when it is, is already contained in the definition of lvalue.
What are some examples of when the object type itself is used, rather than just the expression type?
I am mostly interested in this from the perspective of the standard, but a description on how it might differ from the compiler point of view will be also appreciated.
Value category of an expression
Type of an expression
Both of these require knowing not only the grammar of the expression used, which itself requires knowing whether names are types/templates (C++'s cannot be parsed without doing name lookup and understanding declarations), but also require knowing the value category and types of the operands of the expression. That's not only true for postfix expressions. It also applies e.g. to member access.
Type of an object
This is independent of the types and value categories of expressions as you said. All compile-time properties, such as overload resolution, are determined completely by value category and type of expressions. The result of an expression when evaluated (at runtime) may refer to some object (if it is a glvalue expression). The type of this object is not necessarily the same as the type of the expression and can vary between multiple evaluations of the same expression.
Usually the result of a glvalue expression of a given type is supposed to refer to an object of the same (up to qualifiers) type, but that is not generally guaranteed. Similarly a pointer value of a give pointed-to-type should be a pointer to an object of that same type (up to qualifiers), but that is not guaranteed. Basically the only way to violate these is however to use potentially dangerous casts such as reinterpret_cast.
A declaration int& x = /*...*/; doesn't declare or create any object. A reference is not considered an object. x will refer to the object which is the result of the right-hand side. As above, it may not necessarily have the type int. For example:
alignas(int) char y;
int& x = reinterpret_cast<int&>(y);
now the name x used in an expression has type int and value category lvalue, but the result of the lvalue expression refers to an object of type char (and access through this lvalue is not allowed because it would be an aliasing violation).
Objects are also not only created by declarations. As you said they are a runtime property that can only be talked about when considering a particular state of program execution. Other than from (non-reference) variables, objects can for example be created explicitly as temporary objects (via temporary materialization conversion from prvalues to xvalues), by new expressions or implicitly by certain operations which are defined to do so (e.g. a call to std::malloc under certain (strict) conditions).
It is also possible to reuse storage of objects, even from declared variables, or to nest objects in certain other objects. For example
static_assert(sizeof(float) == sizeof(int));
alignas(float) int x;
new(&x) float;
Now if the assertion succeeds after the new expression the int object from the declaration is not alive anymore and trying to read/write through x with
x = 1;
would have undefined behavior as x refers to an object outside its lifetime, but e.g.
*std::launder(reinterpret_cast<float*>(&x)) = 1;
will be fine after the new expression (but not before), because reinterpret_cast<float*>(&x) is a prvalue expression of type float* with a pointer value pointing to the out-of-lifetime int object, while the additional std::launder call adjusts the pointer value to point to the float object at the same storage location which is inside its lifetime.
(However you generally have to make sure that once the lifetime of the declared object would end normally, e.g. at the end of its scope, that there actually is an object of the correct type alive (and transparently-replacable with the original object) at the storage location. Otherwise you will have undefined behavior. Also, there is usually no practical reason for an example as above. One can simply declare two variables instead and have the compiler figure out whether the same storage can be reused.)
There is also the related concept of dynamic type of an expression, which however is slightly different from all of the above. Consider for example
struct A {
virtual int f() { return 1; }
};
struct B : A {
int f() override { return 2; }
};
B b;
A& a = b;
int x = a.f();
Here a in the last expression is an lvalue of type A and the result of the expression also refers to an object of type A, specifically the A base subobject of b, but the dynamic type of the expression for this specific evaluation is B, the most-derived type of the object to which the result of the expression a refers. This concept is used to determine which function override should be called in virtual dispatch but does not affect the (static) type or value category of the call expression a.f(). Using A& a = reinterpret_cast<A&>(b); instead would change the type of the result of the expression a to B and cause undefined behavior.

Definition ambiguity about whether dereference operator yields object vs value in an expression

Recently, I have started learning C++ and following the first book from this StackOverflow resource. My question is pretty straight forward. The way * (in an expression) is defined as in the three current resources I am referring to learn C++ are as follows.
* in this context is the dereference operator.
C++ Primer says * yields an object.
Wikipedia says * returns an l-value equivalent to the value at the pointer address.
A Tour of C++ says * returns the content of object.
Where, 2 and 3 make complete sense to me but not 1. Can someone dumb it down for me?
In my understanding an object is simply a memory location which holds some value.
However, Can object be thrown as keyword to reflect the value in context to dereferencing a pointer as in 1?
Examples are greatly appreciated.
Edit: Solved, Answers by user eerorika and bolov are perfect and elegant. However eerorika answered first so I will accept it as the answer but all the answers are great as well in their own way. Thanks SO community.
In C++ terminology, an "object" is a value which occupies a region of storage. Here are examples of objects:
int x = 42; // x is an object of type int
int y; // y is an object of type int
char *p = new char; // p points to an object of type char
size_t s = std::string().size(); // s is an object of type size_t; there is also an unnamed temporary object of type std::string involved in the expression
Dereferencing a pointer yields an lvalue which refers to the object to which the point points. So let's take another example:
int i = 42;
int *p = &i;
*p = 314;
The expression *p is an lvalue which references the object i. After the above code, i will have the value 314.
So I'd say that both statements 1 (C++ Primer) and 2 (Wikipedia) are correct, saying the same thing in different words. While 3 is probably correct as well, I consider it somewhat misleading, as you don't normally say an object to have "contents." You could say an object has a value, or that it is a value.
Regarding this statement which was originally present in the question:
In my understanding an object is simply a memory location which holds some value which can be useful or null.
The first part is effectively correct, an object is a memory location which holds some value. However, the second part seems somewhat confused: in C++, you normally speak about null only in the context of pointers. A pointer is a type of object which holds an address of another object (or function), or a special value, the null pointer value, which means "not pointing to anything." Most objects (ints, doubles, chars, arrays, instances of class types) cannot be said to be "null".
They are all wrong. But that's ok, because they are all correct :)
The only real authority here is the standard. And the standard is very precise:
§5.3.1 Unary operators [expr.unary.op]
1 The unary * operator performs indirection: the expression to which
it is applied shall be a pointer to an object type, or a pointer to a
function type and the result is an lvalue referring to the object or
function to which the expression points. If the type of the expression
is “pointer to T,” the type of the result is “T.”
[n4296]
So there you go, the only valid definition is:
the result is an lvalue referring to the object or function to which
the expression points
Anything else is plain wrong.
Except... The standard is useful for compiler implementers and settling down debates of code conformity with the standard. When teaching or just when talking about C++ we generally aren't as rigorous as the standard. We do some simplications for the sake of ... well simplicity (or sometimes just out of ignorance). So we sometimes say "object" when the correct standard terminology would be "lvalue expression referring to an object" and so on.
I would say all the definitions are correct, they just use different terms or take different shortcuts for the sake of easy understanding.
Colloquially, and in this context in particular, value and object are often used as synonyms.
All three descriptions are correct. Wikipedia one is the most precise description out of those three.
Wikipedia says of values:
An lvalue refers to an object that persists beyond a single expression. An rvalue is a temporary value that does not persist beyond the expression that uses it.
C++ standard (draft) says of objects:
The constructs in a C ++ program create, destroy, refer to, access, and manipulate objects. An object is
created by a definition (6.1), by a new-expression (8.3.4), when implicitly changing the active member of a
union (12.3), or when a temporary object is created (7.4, 15.2). An object occupies a region of storage in its
period of construction (15.7), throughout its lifetime (6.8), and in its period of destruction (15.7). ...
And about memory location:
A memory location is either an object of scalar type or a maximal sequence of adjacent bit-fields all having
nonzero width. ...
In my understanding an object is simply a memory location which holds some value which can be useful or null
An object cannot be null in general - unless that object is a pointer, or some other specific type which has a special value described by the word null.
From the docs
The dereference or indirection expression has the form
* pointer-expression
If pointer-expression is a pointer to function, the result of the dereference operator is a function designator for that function.
If pointer-expression is a pointer to object, the result is an lvalue expression that designates the pointed-to object.
So as an example
int x = 5; // x is an lvalue
int* p = &x; // p is a pointer that points to x
*p = 7; // dereferencing p returns the lvalue x, which you can then operate on

c++ access static members using null pointer

Recently tried the following program and it compiles, runs fine and produces expected output instead of any runtime error.
#include <iostream>
class demo
{
public:
static void fun()
{
std::cout<<"fun() is called\n";
}
static int a;
};
int demo::a=9;
int main()
{
demo* d=nullptr;
d->fun();
std::cout<<d->a;
return 0;
}
If an uninitialized pointer is used to access class and/or struct members behaviour is undefined, but why it is allowed to access static members using null pointers also. Is there any harm in my program?
TL;DR: Your example is well-defined. Merely dereferencing a null pointer is not invoking UB.
There is a lot of debate over this topic, which basically boils down to whether indirection through a null pointer is itself UB.
The only questionable thing that happens in your example is the evaluation of the object expression. In particular, d->a is equivalent to (*d).a according to [expr.ref]/2:
The expression E1->E2 is converted to the equivalent form
(*(E1)).E2; the remainder of 5.2.5 will address only the first
option (dot).
*d is just evaluated:
The postfix expression before the dot or arrow is evaluated;65 the
result of that evaluation, together with the id-expression, determines
the result of the entire postfix expression.
65) If the class member access expression is evaluated, the subexpression evaluation happens even if the result is unnecessary
to determine the value of the entire postfix expression, for example if the id-expression denotes a static member.
Let's extract the critical part of the code. Consider the expression statement
*d;
In this statement, *d is a discarded value expression according to [stmt.expr]. So *d is solely evaluated1, just as in d->a.
Hence if *d; is valid, or in other words the evaluation of the expression *d, so is your example.
Does indirection through null pointers inherently result in undefined behavior?
There is the open CWG issue #232, created over fifteen years ago, which concerns this exact question. A very important argument is raised. The report starts with
At least a couple of places in the IS state that indirection through a
null pointer produces undefined behavior: 1.9 [intro.execution]
paragraph 4 gives "dereferencing the null pointer" as an example of
undefined behavior, and 8.3.2 [dcl.ref] paragraph 4 (in a note) uses
this supposedly undefined behavior as justification for the
nonexistence of "null references."
Note that the example mentioned was changed to cover modifications of const objects instead, and the note in [dcl.ref] - while still existing - is not normative. The normative passage was removed to avoid commitment.
However, 5.3.1 [expr.unary.op] paragraph 1, which describes the unary
"*" operator, does not say that the behavior is undefined if the
operand is a null pointer, as one might expect. Furthermore, at least
one passage gives dereferencing a null pointer well-defined behavior:
5.2.8 [expr.typeid] paragraph 2 says
If the lvalue expression is obtained by applying the unary * operator
to a pointer and the pointer is a null pointer value (4.10
[conv.ptr]), the typeid expression throws the bad_typeid exception
(18.7.3 [bad.typeid]).
This is inconsistent and should be cleaned up.
The last point is especially important. The quote in [expr.typeid] still exists and appertains to glvalues of polymorphic class type, which is the case in the following example:
int main() try {
// Polymorphic type
class A
{
virtual ~A(){}
};
typeid( *((A*)0) );
}
catch (std::bad_typeid)
{
std::cerr << "bad_exception\n";
}
The behavior of this program is well-defined (an exception will be thrown and catched), and the expression *((A*)0) is evaluated as it isn't part of an unevaluated operand. Now if indirection through null pointers induced UB, then the expression written as
*((A*)0);
would be doing just that, inducing UB, which seems nonsensical when compared to the typeid scenario. If the above expression is merely evaluated as every discarded-value expression is1, where is the crucial difference that makes the evaluation in the second snippet UB? There is no existing implementation that analyzes the typeid-operand, finds the innermost, corresponding dereference and surrounds its operand with a check - there would be a performance loss, too.
A note in that issue then ends the short discussion with:
We agreed that the approach in the standard seems okay: p = 0; *p;
is not inherently an error. An lvalue-to-rvalue conversion would give
it undefined behavior.
I.e. the committee agreed upon this. Although the proposed resolution of this report, which introduced so-called "empty lvalues", was never adopted…
However, “not modifiable” is a compile-time concept, while in fact
this deals with runtime values and thus should produce undefined
behavior instead. Also, there are other contexts in which lvalues can
occur, such as the left operand of . or .*, which should also be
restricted. Additional drafting is required.
…that does not affect the rationale. Then again, it should be noted that this issue even precedes C++03, which makes it less convincing while we approach C++17.
CWG-issue #315 seems to cover your case as well:
Another instance to consider is that of invoking a member function
from a null pointer:
struct A { void f () { } };
int main ()
{
A* ap = 0;
ap->f ();
}
[…]
Rationale (October 2003):
We agreed the example should be allowed. p->f() is rewritten as
(*p).f() according to 5.2.5 [expr.ref]. *p is not an error when
p is null unless the lvalue is converted to an rvalue (4.1
[conv.lval]), which it isn't here.
According to this rationale, indirection through a null pointer per se does not invoke UB without further lvalue-to-rvalue conversions (=accesses to stored value), reference bindings, value computations or the like. (Nota bene: Calling a non-static member function with a null pointer should invoke UB, albeit merely hazily disallowed by [class.mfct.non-static]/2. The rationale is outdated in this respect.)
I.e. a mere evaluation of *d does not suffice to invoke UB. The identity of the object is not required, and neither is its previously stored value. On the other hand, e.g.
*p = 123;
is undefined since there is a value computation of the left operand, [expr.ass]/1:
In all cases, the assignment is sequenced after the value computation
of the right and left operands
Because the left operand is expected to be a glvalue, the identity of the object referred to by that glvalue must be determined as mentioned by the definition of evaluation of an expression in [intro.execution]/12, which is impossible (and thus leads to UB).
1 [expr]/11:
In some contexts, an expression only appears for its side effects.
Such an expression is called a discarded-value expression. The
expression is evaluated and its value is discarded. […]. The lvalue-to-rvalue conversion (4.1) is
applied if and only if the expression is a glvalue of
volatile-qualified type and […]
From the C++ Draft Standard N3337:
9.4 Static members
2 A static member s of class X may be referred to using the qualified-id expression X::s; it is not necessary to use the class member access syntax (5.2.5) to refer to a static member. A static member may be referred
to using the class member access syntax, in which case the object expression is evaluated.
And in the section about object expression...
5.2.5 Class member access
4 If E2 is declared to have type “reference to T,” then E1.E2 is an lvalue; the type of E1.E2 is T. Otherwise,
one of the following rules applies.
— If E2 is a static data member and the type of E2 is T, then E1.E2 is an lvalue; the expression designates the named member of the class. The type of E1.E2 is T.
Based on the last paragraph of the standard, the expressions:
d->fun();
std::cout << d->a;
work because they both designate the named member of the class regardless of the value of d.
runs fine and produces expected output instead of any runtime error.
That's a basic assumption error. What you are doing is undefined behavior, which means that your claim for any kind of "expected output" is faulty.
Addendum: Note that, while there is a CWG defect (#315) report that is closed as "in agreement" of not making the above UB, it relies on the positive closing of another CWG defect (#232) that is still active, and hence none of it is added to the standard.
Let me quote a part of a comment from James McNellis to an answer to a similar Stack Overflow question:
I don't think CWG defect 315 is as "closed" as its presence on the "closed issues" page implies. The rationale says that it should be allowed because "*p is not an error when p is null unless the lvalue is converted to an rvalue." However, that relies on the concept of an "empty lvalue," which is part of the proposed resolution to CWG defect 232, but which has not been adopted.
The expressions d->fun and d->a() both cause evaluation of *d ([expr.ref]/2).
The complete definition of the unary * operator from [expr.unary.op]/1 is:
The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points.
For the expression d there is no "object or function to which the expression points" . Therefore this paragraph does not define the behaviour of *d.
Hence the code is undefined by omission, since the behaviour of evaluating *d is not defined anywhere in the Standard.
What you are seeing here is what I would consider an ill-conceived and unfortunate design choice in the specification of the C++ language and many other languages that belong to the same general family of programming languages.
These languages allow you to refer to static members of a class using a reference to an instance of the class. The actual value of the instance reference is of course ignored, since no instance is required to access static members.
So, in d->fun(); the the compiler uses the d pointer only during compilation to figure out that you are referring to a member of the demo class, and then it ignores it. No code is emitted by the compiler to dereference the pointer, so the fact that it is going to be NULL during runtime does not matter.
So, what you see happening is in perfect accordance to the specification of the language, and in my opinion the specification suffers in this respect, because it allows an illogical thing to happen: to use an instance reference to refer to a static member.
P.S. Most compilers in most languages are actually capable of issuing warnings for that kind of stuff. I do not know about your compiler, but you might want to check, because the fact that you received no warning for doing what you did might mean that you do not have enough warnings enabled.

Deferencing a returned reference

Given:
int& foo(); // don't care what the reference is to
int intVal;
In the following two cases the right hand side is the same function call
int& intRef = foo();
intVal = foo(); // a reference is returned... a value is assigned.
In the second case how is the returned reference "converted" into a value?
Is it done by the assignment operator for the int?
At the language level there's no such concept as "dereferencing a reference". A reference implements the concept of an lvalue. A variable and a reference are basically the same thing. The only difference between a variable and a reference is that the variable is bound to its location in storage automatically, by the compiler, while a reference is generally bound through user action at run time.
In your example, there's no conceptual difference between intRef and intVal. Both are lvalues of type int. And at the conceptual level both are accessed through the same mechanism. You can even think of all variables in your program as references, which were implicitly pre-bound for you by the compiler. This is basically what Bjarne Stroustrup means in TC++PL when he says (not verbatim) that one can think of references as just alternative names for existing variables.
The only moment when the difference between the two is perceptible is when you create these entities and initialize them. Initialization of a reference is an act of binding it to some location in storage. Initialization of a variable is an act of copying the initial value into the existing storage.
But once a reference is initialized, it acts as an ordinary variable: an act of reading/writing a reference is an act of reading/writing the storage location it is bound to. Taking the address of a reference evaluates to the address of the storage location it is bound to. And so on.
It is not a secret that in many cases a reference is implemented internally as a pointer in disguise, i.e. as an invisible pointer that is implicitly dereferenced for you every time you access it. In such cases (when it is really implemented through a pointer) the dereference is done, again, every time you access it. So, it is not the assignment operator that does it, as you ask in your question. It is the very fact that you mentioned the name of that reference in your code that causes the invisible pointer to get dereferenced.
However, an entity that implements "alternative name for existing variable" does not necessarily require storage for itself, i.e. in a compiled language it is not required to be represented by anything material, like a hidden pointer. This is why the language standard states in 8.3.2 that "It is unspecified whether or not a reference requires storage".
foo is returning some reference to an object of type "int". We won't care about where that "int" came from and we'll just assume it exists.
The first line, int& intRef = foo(), creates intRef which also refers to exactly the same object of type "int" as is referenced by the return value of foo.
The second line, the value of intVal is replaced by the value of the object referred to by the returned reference.
In response to your comments:
You seem to be getting very confused between pointers and references. References are just like aliases for an object. Doing anything to a reference will actually affect the object it refers to.
There is no such thing as dereferencing a reference. You can only dereference pointers. Dereferencing is the act of using the unary * operator to get the object pointed at by a point. For example, if you have a int* p, you can do *p to get the object that it points at. This is dereferencing p.
The only time you can do * on a reference is if the object it refers to is a pointer (or if it overloads operator*). In your case, since foo returns an int&, we can't dereference it. The expression *foo() just won't compile. That's because the return value of foo has type "int" which is not a pointer and doesn't overload operator*.
For all intents and purposes, you can treat the reference returned from foo as simply being the object it refers to. Assigning this value to intVal is really no different to assigning x to intVal in the following code:
int intVal;
int x = 5;
intVal = x;
As I'm sure you understand, intVal is given the value of x. This is defined simply by the standard:
In simple assignment (=), the value of the expression replaces that of the object referred to by the left operand.
No conversion needs to occur at all because both sides of the operator are the same type.
This is really no different to your situation. You just have:
intVal = some_ref_to_int;
Where some_ref_to_int is the expression foo(). The fact that it's a reference doesn't matter. intVal receives the value of the object that the reference denotes.
Assigning to intVal is an assignment-expression defined in 5.17 [exp.ass] in the standard. The grammar rules for an assignment-expression are quite complicated, depending on several other grammar rules, but basically you need a modifiable lvalue on the left hand side of the = operator, and a prvalue expression on the right hand side.
In the case of
intVal = foo();
the expression on the RHS is an lvalue of type int, so the built-in lvalue-to-rvalue conversion takes place ... this is barely a conversion, in that the value doesn't change and neither does the type (except that for fundamental types cv-qualifiers are removed, so if the lvalue is type const int the prvalue will be type int). [conv.lval] says
A glvalue (3.10) of a non-function, non-array type T can be converted to a prvalue. [...] If T is a non-class type, the type of the prvalue is the cv-unqualified version of T. Otherwise, the type of the prvalue is T. [...] the value contained in the object indicated by the glvalue is the prvalue result.
So the prvalue has type int and the same value as foo() i.e. the same value as the variable the returned reference is bound to.
The rules of assignment expressions say:
In simple assignment (=), the value of the expression replaces that of the object referred to by the left operand.
So the value of intVal will be replaced by the value of the prvalue. The rules continue:
If the left operand is not of class type, the expression is implicitly converted (Clause 4) to the cv-unqualified type of the left operand.
So because int is not a class type (and therefore has no overloaded operator= it just uses the built-in assignment operator) the assignment will convert the RHS to int, which is the type it already has in your case.
So the value of intVal gets set to the value of the prvalue, which we said is the value of the glvalue expression foo(), i.e. the value of the variable the reference is bound to.
Note that the lvalue-to-rvalue conversion is nothing to do with the RHS being a reference. The same thing happens here:
int val = 0;
intVal = val;
val is an lvalue of type int so it's converted to a prvalue of type int and the value of intVal is set to the value of that prvalue.
The rules are expressed in terms of an expression's "value category" (i.e. lvalue or rvalue) not whether it's a reference or not. Any "dereferencing" of a reference that's needed is done implicitly and invisibly by the compiler in order to implement the required behaviour.

Why can't you take the address of nullptr?

In the C++11 standard, I don't understand the reason why taking the address of nullptr is disallowed whereas one is allowed to take the address of their own std::nullptr_t instances. Aside from the fact that nullptr is a reserved keyword, is there any designated reasoning for this decision?
Simply because it amuses me, I attempted to circumnavigate this restriction with the following function:
decltype(nullptr)* func(const decltype(nullptr) &nref) noexcept
{
return const_cast<decltype(nullptr)*>(reinterpret_cast<const decltype(nullptr)*>(&nref));
}
I had to use reinterpret_cast on the parameter because without it I was getting the hysterical error:
error: invalid conversion from 'std::nullptr_t*' to 'std::nullptr_t*' [-fpermissive]
When I call this function by passing nullptr directly I get a different address each time. Is nullptr dynamically assigned an address just-in-time for comparisons and such? Or (probably more likely) perhaps is the compiler forcing a temporary copy of the underlying object?
Of course none of this is vital information, I just find it interesting why this particular restriction was implemented (and subsequently why I am seeing the behavior I am).
It's the same as not being able to take the address of 5 even though you can take the address of an int after giving it the value 5. It doesn't matter that there's no alternative value for a nullptr_t to have.
Values don't have addresses; objects do.
A temporary object is generated when you pass such a value to a const & parameter, or otherwise bind a value to a const reference, such as by static_cast< T const & >( … ) or declaring a named reference T const & foo = …;. The address you're seeing is that of the temporary.
If you're after a standard answer, § 18.2/9 puts your observations pretty bluntly:
Although nullptr’s address cannot be taken, the address of another nullptr_t object that is an lvalue can
be taken.
Alternatively, § 2.14.7 says this about nullptr:
The pointer literal is the keyword nullptr. It is a prvalue of type std::nullptr_t.
So what is a prvalue? § 3.10/1 answers that:
A prvalue (“pure” rvalue) is an rvalue that is not an xvalue. [ Example: The result of calling a function
whose return type is not a reference is a prvalue. The value of a literal such as 12, 7.3e5, or true is
also a prvalue. — end example ]
Hopefully, trying to take the address of any of those things in the example will make more sense as to why you can't take the address of nullptr. It's part of those examples!
nullptr is a (literal) constant, and these don't have a memory address, like any other literal constant in your code. It's similar to 0, but of the special std::nullptr_t type instead of void* to avoid problems with overloading (pointers vs. integers).
But if you define your own variable with the value nullptr, it has a memory address, so you can take its address.
The same holds for any other literal constant (which in C++ fall under the category prvalue) of any other type, since literal constants aren't stored in your program (only as parts of expressions where they occur), that's why it doesn't make any sense to talk about addresses. However, constant variables do have addresses, to point out the difference.
Both true and false are keywords and as literals they have a type ( bool ). nullptr is a pointer literal of type std::nullptr_t, and it's a prvalue (you cannot take the address of it using &), also nullptr is prvalue so you can't take its address,literal constants are not stored in your program.
It doesn't make sense to have address.