I'm coming from a C# background and trying to learn a little C++.
I came across the following lines:
int x[3] = { 1, 2, 3 };
int*&& y = x;
int* z = y;
I know what pointers and arrays are and have some small understanding on lvalue and rvalue references. However I can't wrap my head around what int*&& y = x; actually does.
It would read like it creates a pointer to an rvalue reference, is this correct? What would be the use case of something like that, e.g. what is going on in memory if we execute this?
int*&& y = x; declares y to be a rvalue reference to pointer to int and initializes it with x. (Note that pointer to reference types (e.g. int&&*) do not exist in C++.)
Now the issue is that x is an array of int, not a pointer to int. So the reference can't bind directly to x. The types are not reference-compatible.
In such a situation (if the reference is either a rvalue reference or a const lvalue reference) an (unnamed) temporary object of the referenced type is created and the reference binds to that object instead. In this case an int* object is created and initialized with x, which by array-to-pointer decay means that the int* temporary object will be initialized to point to the first element of the array x.
Temporary objects normally live only until the end of the (full-)expression in which they are created, but in this case, because a reference is immediately bound to it, so-called temporary lifetime extension applies and the temporary int* object will live as long as the reference does (i.e. until the end of the reference's scope).
In other words it is (mostly) equivalent to
int* /*unnamed*/ = x;
int*&& y = /*unnamed*/;
where the comment /*unnamed*/ is supposed to represent the non-existing name of the temporary object.
When a reference's name is used in an expression (and this is completely independent of whether or not it is a lvalue or rvalue reference), it behaves exactly the same as if the object to which the reference is bound would have been named instead (but with the type of the reference with reference-qualifiers stripped which may e.g. differ by a const).
In other words int* z = y; behaves equivalently to int* z = /*unnamed*/;. So z is intialized from the temporary int* object, which has a pointer value pointing to the first element of the x array. For scalar types like int* initialization is simply copying the value, so z will also be initialized to point to the first element of x.
The whole thing is needlessly convoluted. It is exactly equivalent to int* z = x;. Using rvalue references usually only really makes sense as function parameters and some constructs where type deduction occurs. The important difference between lvalue and rvalue references is that they affect overload resolution differently and that they may be initialized with different value categories. There is also one special case in type deduction where rvalue references behave differently (as so-called forwarding references). Aside from that there is no difference between the different kinds of references.
what is going on in memory if we execute this?
That's mostly an implementation detail that shouldn't matter. You have an array that is stored somewhere depending on where you put these lines in your program. It is likely to be physically present somewhere in memory if the compiler doesn't figure it isn't needed. The reference y, the temporary int* which it points to and the z pointer may or may not actually be physically present in some memory, but the compiler is likely to just reduce all of them directly to the array. In particular, on the language level, references are not object and do not have storage (e.g. they don't have a memory size or location). If the compiler needs some memory to implement them (e.g. as a pointer), then that is purely an implementation detail of the compiler.
Related
I'm new to the idea of reference in C++, I have a question concerning the memory allocation of reference to a pure number constant. (Another thing I want to check first is that I suspect const reference, which I frequently came across, means reference to const, but I'm not sure.)
Here is my testing on ideone.com:
#include <stdio.h>
int main() {
const int r0 = 123;
const int &r1 = 123;
const int &r2 = 123;
const int &r3 = r2;
printf("%p\n", (void *)&r0);
printf("%p\n", (void *)&r1);
printf("%p\n", (void *)&r2);
printf("%p\n", (void *)&r3);
return 0;
}
and the result:
0x7ffee3bd74c4
0x7ffee3bd74c8
0x7ffee3bd74cc
0x7ffee3bd74cc
The reason r2 is the same as r3 is clear from this answer - How does a C++ reference look, memory-wise?, which says it's depending on compiler. But I'm thinking about why compiler doesn't also make r0,r1,r2 all the same, since all have the same pure constant value 123. (or called prvalue if no wrong search)
As a note: After some search on this site, I found a most related question - but in python. Although different language but I thought the idea should be the same/similar: from the link, if my program were written in python then there will be only one 123 is in the memory space for saving space.
Some other answers I've read:
C++ do references occupy memory: This answer suggests that if it's necessary then int &x is implemented as *(pointer_to_x).
How does a C++ reference look, memory-wise?: This answer suggests that compiler will try its best to save space.
Your 123 isn't a "constant". Rather, it is a literal. A literal forms an expression that is a prvalue (i.e. a temporary object initialized with the value of given by the literal). When you bind that expression to are reference, the lifetime of that object is extended to that of the reference, but the important point here is that each such object is a distinct object, and thus has a distinct address.
If you will, the text string "123" provides a rule for how to create objects, but it is not by itself an object. You can rewrite your code to make this more explicit:
const int & r = int(123); // temporary of type "int" and value "123"
(There's no single such thing as "a constant" in C++. There are lots of things that are constant in one way or another, but they all need more detailed consideration.)
The literal is not an object. The references do not refer to the literal. When you initialise a reference using a literal, a temporary object will be created, and the lifetime of the temporary object is bound to the lifetime of the reference.
The objects (one local variable, two temporaries) are separate and distinct objects despite having the same value. Since they're separate, they occupy separate memory locations. The standard mandates this, and that makes it possible to identify and distinguish objects based on their memory address.
The three declaration statements:
const int &r1 = 123;
const int &r2 = 123;
const int &r3 = r2;
will initialize 3 temporary objects with lifetime extended to be equal to the scope of their respective variables. Now, there is a language rule that says:
Any two objects with overlapping lifetimes (that are not bit fields)
are guaranteed to have different addresses unless one of them is a
subobject of another or provides storage for another, or if they are
subobjects of different type within the same complete object, and one
of them is a zero-size base.
Since the references are bound to 3 distinct temporary, then you cannot observe these objects on overlapping addresses.
Interestingly, the As-if rule might probably permit the program to allocate all three temporary objects at the same address but only if your compiler and linker can theoretically prove that your program can never observe the these objects as allocated at the same address. In your example, this is infeasible since you print the address of the objects.
Consider this toy example:
struct A { int x; };
int main() {
A&& a=A();
return 0;
}
Is there an implicit cast from temporary values (values of built-in type, and temporary objects) to objects of type rvalue reference?
Does a has an address?
Is there an implicit cast from temporary values (values of built-in type, and temporary objects) to objects of type rvalue reference?
First a nitpick, casts are explicit conversions by definition, so there cannot be an implicit cast. Beyond that, a reference is distinctly not an object type. It may even not require any storage. References are bound to objects. It'd be more appropriate to think about them as alternative names of objects. Rvalue references in particular can only bind to objects of a particular sort. Which are roughly all "nameless" and "temporary" values. We may also cast to an rvalue reference to force an object being designated as "about to expire".
There's however an intricate point here, once an rvalue reference binds to an object, it's no longer "nameless". In particular, this means that by using that reference we call an object "by name" and thus obtain an lvalue. Furthermore, and to coincide with that, binding a reference in block scope to a temporary, prolongs the lifetime of the temporary until the end of the scope. And that brings us to the next point.
Does a has an address?
Yes and no. The refernece itself may or may not require storage, as I said previously. But that's moot, since it's just the name of an object. Taking the address of a reference will yield the address of the object that was bound to it. In this case, it will be the address of the temporary bound to it.
I am currently learning C++ from C++ Primer, and it explains how a reference is an alias to another variable name. It also explains how a pointer points to another variable. It states that the difference between a pointer and a reference is that pointers can be reassigned and references can't.
In the following code example, what can I do with the pointer or reference that I can't do with the other?
double pi = 3.14;
double &piRef = pi;
double *const piPnt = π
//both of these examples are valid and do the same thing
piRef = 3.14159;
*piPnt = 3.14159;
//however, if I attempt to reassign what the pointer points to, it is illegal.
//this is the same as with a reference, as a reference can't be reassigned either
double tau = 6.28;
piPnt = τ
I am aware of the internal differences of each (such as that a pointer is an object, a reference isn't). I am interested in how those differences matter to the programmer beyond a slightly different syntax. As such, this is not a duplicate of this question in which the accepted answer only talks about internal differences.
From a functional point of view pointers and references are indeed the same thing... they reference an object and are not a copy of that object.
The only real difference in addition to not being able to rebind a reference is that a pointer can be NULL (i.e. it can point to nothing) while a reference is assumed to always reference an object.
You technically can actually end up with a reference that is referencing no object (e.g. passing *p to a function expecting a reference where p is the null pointer) but this is "undefined behavior".
In other words pointers are more "flexible" than references and this allows the compiler to ignore
That a reference can change the object it's referencing
That a reference can have no object
And this can in some cases produce faster code (however for the second point de-referencing a null pointer is undefined behavior and not a runtime error; the compiler therefore is not mandated to generate code that does something special in this case: that a pointer can actually point to no object is indeed irrelevant from a code generation point of view because it's in the contract between programmer and compiler that this will never happen).
The "price" to pay for the added flexibility of rebinding and having NULLs is that the syntax is (somewhat gratuitously) more annoying.
If the pointer cannot be reassigned (because the pointer itself is const) then there are no practical differences whatsoever except for the more verbose-but-explicit syntax. This because despite being an object a const-declared pointer cannot be altered even using aliasing.
While from a syntactic point of view you can take the address of the const pointer, cast the address to an address of a non-const pointer and change the value pointed to, such an operation would be undefined behavior and whatever happens (e.g. ignoring the assignment) the compiler is going to be right if taken to court :-)
what can I do with the pointer or reference that I can't do with the other?
References allow you to write certain constructors and overload operators:
class X
{
// copy constructor
X(const X& a);
// move constructor
X(X&& a);
// copy assignment operator
X& operator=(const X& a);
// move assignment operator
X& operator=(X&& a);
}
(Indeed, operator overloading was the motivating use case for introducing references into C++.)
What is often overlooked is the fact that modern C++ distinguishes between X& (a reference to an lvalue) and X&& (a reference to an rvalue), but there is no such thing as a pointer to an rvalue.
what can I do with the pointer or reference that I can't do with the
other?
double *const piPnt = π
The above statement
marks piPnt as a read only variable in memory layout
So, piPnt = &xyz would throw an error now.
But changing the value at the address the pointer points to is still valid.
That is , *piPnt = 56 is fine.
Const Pointers are useful in embedded systems that need to refer to the same memory (port mapping). It's a one time mapping and constant pointers are helpful here.
Now with regards to references:
double &piRef = pi;
You cannot reinitialize a reference in C++. You can assign different value to the object it refers to. This is one and the same object for that reference forever. And this is what you did in your example.
piRef = 3.14159;
A reference cannot be changed to refer to another object after
initialization. Note that initialization of a reference is treated
very differently from assignment to it. Argument passing (5.2.2) and
function value return (6.6.3) are initializations.
Some places where references are useful:
Pointers cannot point to temporaries, the standard expressly forbids doing it. References can bind to temporaries.
A pointer can be NULL while a reference is assumed to always reference an object. You can still return null from a function returning a reference, the compiler would not complain about it, but that is suicidal.
When you do this:
int square(int& x) { return x*x;};
int s = square(40); // This gives error
To fix this you do this:
int square(const int& x) { return x*x;};
int s = square(40); // This is OK
I understand that 40 is a constant but so what if I do this:
const int& x = 40
Why would that be okay only with const keyword? Is that the way the compiler protect that no one can change the value referred to by x?
40 is a const so we don't even know its location in memory, but shouldn't the compiler know this address, therefore changing the value from 40 to 30 for example should be allowed since the compiler can just go to address &40 and change the value to 30?
Just because it's possible to implement doesn't mean you should do it. Having 40 really be 30 is hilarious but especially unmaintainable and should not be permitted. In addition, 40 doesn't necessarily actually have an address. Consider a 40 in cache, register, or an immediate instruction.
The declaration of square:
int square(int& x);
takes a lvalue, while invocation of square(40) takes a rvalue, this is inconsistent, see more of lvalue and rvalue here.
The rules about references is that you cannot bind a temporary to non-const lvalue reference. The literal 40 can be used to initialize a temporary but it is, itself, not an object. Thus, you cannot bind a non-const lvalue reference to it.
When you do
int const& value = 40;
you actually bind a temporary object initialized to be 40 to the reference value. Normally, temporaries go out of scope and are destroyed at the end of a full-expression. However, when you directly bind a temporary to a non-const reference its life-time is extended to match the life-time of the reference.
The rules prohibiting binding of temporary objects to non-const references is in place primarily because it would probably cause many surprising results. It could technically be done but would be quite likely to produce non-obvious results.
You're confusing constants and literals. They are similar, but not equivalent. 40 is not a constant, it's a literal.
You can't pass a literal by reference, since if you pass something by reference, it means it can be modified - literals cannot. Consider the following:
void foo(int &i)
{
i = 1;
}
foo(0); // What on Earth? 0 == 1?
If you, however, pass a reference to a constant, it means that even if it's a reference, the function is not permitted to modify its argument (since it's a constant), so now you can safely pass in a literal - it now makes sense, since there's no possibility for the function modifying its argument.
You can still do:
int x = 40;
int s = square(x)
x = 30;
s = square(x);
with both versions of square (the one or without const).
When you pass something by reference you are passing an existing object (because that is what a reference means an alias to an existing object).
In your example:
int s = square(30);
You are NOT passing an object. This is a literal (they are not objects). The compiler can convert literals into object by creating a temporary object. But the language has explicit restrictions on temporary object that mean they are const. This means references can not be passed to an interface where they will be mutated (though you can pass them by const reference).
I have been coding in C++ for past few years. But there is one question that I have not been able to figure out. I want to ask, are all temporaries in C++, rvalues?
If no, can anyone provide me an example where temporary produced in the code is an lvalue?
No.
The C++ language specification never makes such a straightforward assertion as the one you are asking about. It doesn't say anywhere in the language standard that "all temporary objects are rvalues". Moreover, the question itself is a bit of misnomer, since the property of being an rvalue in the C++ language is not a property of an object, but rather a property of an expression (i.e. a property of its result). This is actually how it is defined in the language specification: for different kinds of expressions it says when the result is an lvalue and when it is an rvalue. Among other things, this actually means that a temporary object can be accessed as an rvalue as well as an lvalue, depending on the specific form of expression that is used to perform the access.
For example, the result of literal 2 + 3 expression is obviously an rvalue, a temporary of type int. We cannot apply the unary & to it since unary & requires an lvalue as its operand
&(2 + 3); // ERROR, lvalue required
However, as we all know, a constant reference can be attached to a temporary object, as in
const int &ri = 2 + 3;
In this case the reference is attached to the temporary, extending the lifetime of the latter. Obviously, once it is done, we have access to that very same temporary as an lvalue ri, since references are always lvalues. For example, we can easily and legally apply the unary & to the reference and obtain a pointer to the temporary
const int *pi = &ri;
with that pointer remaining perfectly valid as long as the temporary persists.
Another obvious example of lvalue access to a temporary object is when we access a temporary object of class type through its this pointer. The result of *this is an lvalue (as is always the case with the result of unary * applied to a data pointer), yet it doesn't change the fact that the actual object might easily be a temporary. For a given class type T, expression T() is an rvalue, as explicitly stated in the language standard, yet the temporary object accessed through *T().get_this() expression (with the obvious implementation of T::get_this()) is an lvalue. Unlike the previous example, this method allows you to immediately obtain a non-const-qualified lvalue, which refers to a temporary object.
So, once again, the very same temporary object might easily be "seen" as an rvalue or as an lvalue depending on what kind of expression (what kind of access path) you use to "look" at that object.
Prasoon Saurav already linked a very good clc++ thread. In there, James Kanze explains why the question doesn't really make sense. It boils down to:
rvalue-ness is a (boolean) property of expressions - each expression is either an lvalue or an rvalue
temporaries are not expressions
For that reason, the question doesn't make sense.
A good example is the following code:
int main() {
const int& ri = 4;
std::cout << ri << std::endl;
}
The temporary int with value 4 is not an expression. The expression ri that's printed is not a temporary. It's an lvalue, and refers to a temporary.
well, that array operator returns a reference, any function that returns a reference could be considered to do the same thing? all references are const, while they can be lvalues, they modify what they reference, not the reference itself. same is true for the *operator,
*(a temp pointer) = val;
I swear I used to use some compiler that would pass temp values to any function that took a reference,
so you could go:
int Afunc()
{
return 5;
}
int anotherFunc(int & b)
{
b = 34;
}
anotherFunc(Afunc());
can't find one that lets you do that now though, the reference has to be const in order to allow passing of temp values.
int anotherFunc(const int & b);
anyway, references can be lvalues and temporary, the trick being the reference it's self is not modified, only what it references.
if you count the-> operator as an operator, then temporary pointers can be lvalues, but the same condition applies, its not the temp pointer that would be changed, but the thing that it points to.
An array indexing operation is both a temporary and an lvalue, something like a[10] = 1 is an example of what you're looking for; the lvalue is a temporary, calculated pointer.
Short answer: yes, but I'm not going to quote the standard, because proving the point would require addressing every kind of temporary there is. By definition a temporary has a lifetime of one statement, so assigning things to one would be poor style at best.
Interesting answer: Copy elision can make (often makes) a temporary object identical with an lvalue object. For example,
MyClass blah = MyClass( 3 ); // temporary likely to be optimized out
or
return MyClass( 3 ); // likely to directly initialize object in caller's frame
Edit: as for the question of whether there is any temporary object in those cases, §12.8/15 mentions
the copy operation can be omitted by constructing the temporary object directly into the target of the omitted copy
which would indicate that there is a temporary object which may be identical with an lvalue.
It depends on what you consider a temporary variable is. You can write something like
#include <stdio.h>
int main()
{
char carray[10];
char *c=carray+1;
*(c+2+4) = 9;
printf("%d\n",carray[7]);
return 0;
}
This runs in VisualStudios and GCC. You can run the code in codepad
I consider (c+2+4) a rvalue although i want to assign to it. When i dereference it, it would become an lvalue. So yes all temporaries are rvalues. But you can make rvalues (thus a temporary) into an lvalue by dereferencing it
If no, can anyone provide me an example where temporary produced in the code is an lvalue?
The following code binds a constant reference to a temporary object of type const float created by the compiler:
int i;
const float &cfr = i;
The behaviour is "as if":
int i;
const float __tmp_cfr = i; // introduced by the compiler
const float &cfr = __tmp_cfr;