Deferencing a returned reference - c++

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.

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.

Why can't casting an address to int* be an lvalue but casting to a struct pointer can?

I suspect this is true for all primitive types in C/C++.
For example, if you do this:
((unsigned int*)0x1234) = 1234;
The compiler will not let it pass. Whereas if you do this
((data_t*)0x1234 )->s = 1234;
where data_t is a struct, the compiler allows it.
This seems to be the case for at least two compilers I experimented on, one ARM GCC, one TDM-GCC.
Why is this?
The first code snippet doesn't work because the left hand side is not an lvalue. It is only a pointer value, and pointers by themselves are not lvalues.
The second code snippet works because a pointer is being dereferenced, and a dereferenced pointer is an lvalue. It may not be immediately clear from the syntax this is the case, so let's rewrite this:
((data_t*)0x1234 )->s = 1234;
As:
(*(data_t*)0x1234).s = 1234;
Now we can see that the value which is casted to a pointer is dereferenced to an lvalue of struct type, and a member of that struct is subsequently accessed and assigned to.
This is described in section 6.5.2.3p4 of the C standard regarding the -> operator:
A postfix expression followed by the -> operator and an identifier
designates a member of a structure or union object. The value
is that of the named member of the object to which the first
expression points, and is an lvalue. If the first expression is a
pointer to a qualified type, the result has the so-qualified
version of the type of the designated member.
Regarding the first snippet, section 6.5.4p5 regarding the typecast operator states:
Preceding an expression by a parenthesized type name converts
the value of the expression to the named type. This construction is
called a cast. 104) A cast that specifies no conversion has
no effect on the type or value of an expression.
Where footnote 104 states:
A cast does not yield an lvalue. Thus, a cast to a qualified
type has the same effect as a cast to the unqualified version
of the type.
So this describes why the first snippet won't compile but the second snippet will.
However, treating an arbitrary value as a pointer and dereferencing it is implementation defined behavior at best, and most likely undefined behavior.
Your examples are:
((unsigned int*)0x1234) = 1234;
((data_t*)0x1234 )->s = 1234;
Neither ((unsigned int*)0x1234) nor ((data_t*)0x1234 ) is an lvalue, and you can't assign to either of them.
More generally, the prefix of -> doesn't have to be an lvalue. But prefix->member is always an lvalue, whether prefix is or not. Similarly, *p is an value whether p is an lvalue or not.

const_cast<char *>(char* const) not lvalue?

When compiling the code below, I am getting an error on line 3 about the result of const_cast not being an lvalue. Is this only a problem because I used gcc 7.x (even though it is supposed to be fully C++17 compliant)? Or is this indeed invalid code according to the standard?
The code below is a minimal example that triggers the error. Tried gcc 7.1, 7.4, and also https://www.onlinegdb.com/online_c++_compiler and got the same error.
char* const a = "xyz";
char* b;
const_cast<char*>(a) = b; // not lvalue error
The precise error gcc gives is: "error: lvalue required as left operand of assignment".
NOTE (forgot to add): the example has nothing to do with actual code I would ever write. It is an example I came about which (I presume) was created to test how well people understand the standard. So I am only interested in precisely what I asked in the question, i.e., whether this is valid code or not (and why). Thx!
So I am only interested in precisely what I asked in the question, i.e., whether this is valid code or not
It's not. The result of const_cast is a glvalue (lvalue or xvalue) only when casting to a reference type.
[expr.const.cast] (emphasis mine)
1 The result of the expression const_­cast<T>(v) is of type
T. If T is an lvalue reference to object type, the result is an
lvalue; if T is an rvalue reference to object type, the result is an
xvalue; otherwise, the result is a prvalue and the
lvalue-to-rvalue, array-to-pointer, and function-to-pointer standard
conversions are performed on the expression v. Conversions that can be
performed explicitly using const_­cast are listed below. No other
conversion shall be performed explicitly using const_­cast.
You don't cast to a reference type, so the result is a prvalue; not something you may assign to. And don't go casting to a reference type either; attempting to modify an object declared as const gives undefined behavior. Your program will be another sort of invalid then.
First, char* const a = "xyz"; is illegal. a string literal has the type const char[N] and assign it to a char * const removes the constness of the characters which is illegal in an implicit cast.
Now lets pretend that it's fine and lets look at
const_cast<char*>(a) = b
This has two issues. The first is that const_cast<char*>(a) results in a rvalue. For non-class types you cannot assign to rvalues. You would need const_cast<char*&>(a) in order to have an lvalue to assign to, and that brings up the next problem. You can't assign to an object that is const. Stripping away the const using const_cast doesn't fix the issue. It is still not allowed per [dcl.type.cv]/4
Any attempt to modify ([expr.ass], [expr.post.incr], [expr.pre.incr]) a const object ([basic.type.qualifier]) during its lifetime ([basic.life]) results in undefined behavior.
Even with the proper cast, the underlying object is still const so you violate the above clause and have undefined behavior.
The type char * const a defines a pointer variable a, which cannot be changed, but points to characters that can be changed. This is not a common use to make the pointer constant.
The error is telling you that you cannot update the value of a - it's not an lvalue, and I don't believe that const_cast gets around that in this case.
Could you possibly mean const char *a, which allows the pointer itself to be changed, but not the things pointed to?
An "lvalue" is a syntactic construct, meaning a kind of expression that can appear on the left of an assignment. You can assign to a variable, or an array component, or a field, but it's a syntax error to write an assignment to other kinds of expression such as x + y = 7; or f(x) = 5;. A function call such as const_cast<char*>(a) is not a kind of expression which can be assigned to.
It would be syntactically valid to write a = const_cast<char*>(b);, where the function call appears on the right of the assignment.

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

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

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.