C++: Why I can change the assignment of reference on my computer? - c++

I am new to C++, and I am studying the concept of reference.
I have studied the fact that a reference should not be assigned to something else once defined and initialized, the description of reference on C++ primer states that once you have bound a reference to an object, then you can't change the object which the reference is bounded to.
I am confused by this statement. I tried the following code on my computer, the compiler didn't report neither warnings nor errors.
int a = 2;
int b = 4;
int &ref = a;
cout << "ref = " << ref << endl;
ref = b;
cout << "ref = " << ref << endl;
On my computer, the compilation passes, and outputs were printed, one is 2, the other is 4, there's no problem at all.
I am confused by this concept of reference, is it the condition that we SHOULD NOT change the object which the reference is bounded to, or we CANNOT change the object which the reference is bounded to?
P.S. Even the page 58 on C++ primer also demonstrates the example where the reference refers to new object, and the book says it's ok to do that. Like this:
int i = 42;
int *p;
int *&r = p;
r = &i; // isn't this changing r to an new object?
*r = 0;

then you can't change the object which the reference is bounded to.
I am confused by this statement
The statement tries to say, that the reference cannot be modified to refer to another object. The object that is referred can be modified.
When left hand operand of assignment is a reference, you are indirecting through the reference, and assigning the value of the referred object.
ref = b;
ref still refers to the object named by a. That object is modified by this assignment. After the assignment, the value of a is 4.
r = &i; // isn't this changing r to an new object?
r still refers to the object named by p. That object is modified by this assignment. After the assignment, p points to i.

Related

c++ reference function parameter seems to contain a copy of the original object, not acts as a "real reference"

sorry for the newbie question. :)
the reference function parameter is connected to an object, but a modification on the reference has no effect to the original object.
Simplyfied code:
struct dummy{
int i;
};
dummy arr[4];
// fill array
for(int n=0;n<4;n++){
arr[n].i = n;
}
void foo(dummy& d){
d = arr[0];// d.i == 0, so far so good
d.i += 10; // d.i == 10, but arr[0] is still 0
}
...
dummy p;
foo(p);
p.i += 5; // p.i == 15, but arr[0] is still 0
If i use int instead of dummy, it is clear that the original won't change. But if just a property of an object is changed (object is still the same), i assume that the modification was made on the original object.
But not. It seems that d = arr[0]; is just a copy.
Why?
I know, that this scenario can be done with pointers: void foo(dummy** d){...} but i wonder the reason of this reference behavior. A reference is "real" if it is defined in classic way: dummy &a=arr[0], but as function parameter: no.
So I think your misunderstanding is over the line
d = arr[0];
You think that makes d refer to arr[0] but it doesn't. Instead it assigns arr[0] to whatever d is referring to.
In C++, once a reference has been bound it cannot be rebound. Any attempt to assign to a reference always assigns to what the reference is refering to, it never makes the reference refer to something else.
Once initialized references cannot be rebound. The reference d in the function refers to the dummy p that you use to call the function. When you write
d = arr[0];
...that does not make d refer to arr[0]. Instead it assigns arr[0] to p. In other words, it makes a copy.

Assigning double to const int& vs int to const int& [duplicate]

This question already has answers here:
Temporary initialization and Reference initialization
(3 answers)
Closed 6 months ago.
I am trying to understand constant reference in C++ and I stumbled across this problem:
When I assign double to a const int& and then change the value of the referencing double, the value of my int reference stays constant.
double i = 10;
const int &ref = i;
i = 20;
cout << "i: " << i << endl; // i = 20
cout << "&ref: " << ref << endl; // ref = 10
Whereas while assigning int, the value gets changed.
int i = 10;
const int &ref = i;
i = 20;
cout << "i: " << i << endl; // i = 20
cout << "&ref: " << ref << endl; // ref = 20
What is the reason for this behaviour? My guess is that when assigning double, the implicit conversion to int creates a new object and its reference is then being assigned, however I can't find it written down anywhere.
Even though it looks like, ref isn't a reference to i.
double i = 10;
const int &ref = i; // ***
i = 20;
A reference to an int cannot refer to a double. Therefore, in the marked line, a conversion from a double to an int takes place and the result of conversion is a temporary rvalue. Normaly, they are destroyed when the full expression they were created in ends. But there's a rule in C++ that allows extending the lifetime of a temporary when it is bound to a reference to const. It is extended until the reference dies. For that, the compiler actually allocates some memory for it behind the scenes.
The assignment modifies the original object i, the unnamed temporary stays intact.
When you convert the double to an int, the result is an rvalue of type int. This temporary value is bound to a constant reference variable, and thus the lifetime of the temporary value is extended to that of the reference variable. Changing the original double has no effect on that.
The compiler can help you a lot only if you remove const from both declarations and attempt to compile the code snippets:
double i = 10;
/*const*/ int &ref = i; //ERROR - WHY?
This code will give compilation error! Why?
But this code:
int i = 10;
/*const*/ int &ref = i; //NO ERROR
will compile fine.
If the first code compiles fine when there is const, why doesn't it compile now (when const is not there)? What is going on under the hood?
Well, the reason is, actually int& is a reference type which can refer to an object of type int only, not double, so a temporary object of type int is created out of the expressioni (which is double), but the temporary object cannot bind to int& , so it gives compilation error, but as soon as you put const there, it compiles fine, because a temporary object of type int can bind to const int&.
No such thing happens in the second code — no temporary object is created. So when you set i to 20, the ref also changes in the second case (as it refers to i) but in the first case, when you set i to 20, the ref does not change because ref does not refer to i anymore — it rather refers to the temporary object (which you're not changing!). That explains the behaviors of your code snippets.
Hope that helps.
double i = 10;
const int &ref = i;
Depending on the compiler you are using this code, as is, could as well give a compilation error.
On a compiler where it throws an error, if you cast it to (int &) you would see undefined behavior, since you are reading an int from a double's location.
On a compiler which accepts this line, my best guess is it is creating a temporary variable (an rvalue) and assigning to the int reference, and any changes to the original variable are not reflected in the reference.
For the reference to work make sure the source data type can be represented by destination data type.
You can think of an int as a class, and int(double); as a way of constructing an int from a double, you have a const reference to that int, which was constructed from a double.

c++ references appear reassigned when documentation suggests otherwise

According to this question you can't change what a reference refers to. Likewise the C++ Primer 5th Edition states
Once we have defined a reference, there is no way to make that reference
refer to a different object. When we use a reference, we always get the object to
which the reference was initially bound.
However the following code compiles and prints the value 4 which looks to me like the reference was changed?? Please elaborate if this is or is not so.
int a = 2;
int b = 4;
int &ref = a;
ref = b;
cout << ref;
You are not reassigning a reference. A reference acts as an alias for a variable. In this case, ref is an alias for a, so
ref = b;
is the equivalent of
a = b;
You can easily check that by printing out the value of a:
std::cout << a << std::endl; // prints 4
You can understand how references work by comparing their behavior to that of a pointer. A pointer can be thought of as the name of the address of a variable; however a reference is just the name of the variable itself--it is an alias. An alias, once set, can never be changed whereas you can assign a pointer a new address if you want. So you have:
int main(void)
{
int a = 2;
int b = 4;
int* ptr_a = &a;
int& ref_a = a;
ptr_a = &b; //Ok, assign ptr_a a new address
ref_a = &b; //Error--invalid conversion. References are not addresses.
&ref_a = &b; //Error--the result of the `&` operator is not an R-value, i.e. you can't assign to it.
return 0;
}

Weird behavior when using pointers

When I run this code on MS VS C++ 2010:
#include <iostream>
int main() {
const int a = 10;
const int *b = &a;
int *c = (int *)b;
*c = 10000;
std::cout << c << " " << &a << std::endl;
std::cout << *c << " " << a << " " << *(&a) << std::endl;
return 0;
}
The output is:
0037F784 0037F784
10000 10 10
The motivation for writing that code was this sentence from "The C++ Programming Language" by Stroustrup:
"It is possible to explicitly remove the restrictions on a pointer to const by explicit type conversion".
I know that trying to modify a constant is conceptually wrong, but I find this result quite weird. Can anyone explain the reason behind it?
Let's start with the obvious: some of this is platform and compiler dependent.
For starters, see this article on Explicit Type Conversion, and particularly:
A pointer to an object of a const type can be cast into a pointer to a
non-const type. The resulting pointer will refer to the original
object. An object of a const type or a reference to an object of a
const type can be cast into a reference to a non-const type. The
resulting reference will refer to the original object. The result of
attempting to modify that object through such a pointer or reference
will either cause an addressing exception or be the same as if the
original pointer or reference had referred a non-const object. It is
implementation dependent whether the addressing exception occurs.
So this, explains why it may let you modify the variable without bitching.
Note that you could achieve the same using the cast operators directly, as that's what the compiler will do for you as explained in this article on cast operators, with their order of precedence given.
However, the real trick here is in the memory model. A statically allocated variable like a const int a may actually never have any "physical" location in memory, and is just replaced in place at compile time. (I'm trying to put my finger on the actual reference for this, but so far the closest and best I could grab was this (very nice) SO answer to is memory allocated for a static variable that is never used? - If anyone finds the actual reference, please let us know.)
So here the compiler is simply humoring you, and trying to make some sense of your pointer arithmetic as much as it can, but in the end substitutes a actual values for the 2 last parts of your 2nd cout call.
The reason is that this is undefined behaviour.
The Stroustrup quote likely refers to the case where the object was not declared const but you have only a const pointer to it.
i.e. This is well-defined (using c-style cast as they appear in question):
int a{10};
const int* pa = &a;
int* b = (int*)pa;
*b = 5;
And this is undefined:
const int a{10};
const int* pa = &a;
int* b = (int*)pa;
*b = 5;
Attempting to modify an object declared const, however you get a non-const pointer to it, is UB.

Why can I assign a new value to a reference, and how can I make a reference refer to something else?

I have couple of questions related to usage of references in C++.
In the code shown below, how does it work and not give a error at line q = "world";?
#include <iostream>
using namespace std;
int main()
{
char *p = "Hello";
char* &q = p;
cout <<p <<' '<<q <<"\n";
q = "World"; //Why is there no error on this line
cout <<p <<' '<<q <<"\n";
}
How can a reference q be reinitialized to something else?
Isn't the string literal, p = "Hello", a constant or in read-only space? So if we do,
q = "World";
wouldn't the string at p which is supposed to be constant be changed?
I have read about C++ reference type variables as they cannot be reinitialized or reassigned, since they are stored 'internally' as constant pointers. So a compiler would give a error.
But how actually a reference variable can be reassigned?
int i;
int &j = i;
int k;
j = k; //This should be fine, but how we reassign to something else to make compiler flag an error?
I am trying to get hold of this reference, and in that maybe missed some key things related, so these questions.
So any pointers to clear this up, would be useful.
a) It cannot, the line you quote doesn't change the reference q, it changes p.
b) No the literal is constant, but p is a pointer which points at a literal.
The pointer can be changed, what is being pointed to cannot.
q = "world"; makes the pointer p point to something else.
You seem to think that this code
int i;
int &j = i;
int k;
j = k;
is reassigning a reference, but it isn't.
It's assigning the value of k to i, j still refers to i.
I would guess that this is your major misunderstanding.
An important detail about references that I think you're missing is that once the reference is bound to an object, you can never reassign it. From that point forward, any time you use the reference, it's indistinguishable from using the object it refers to. As an example, in your first piece of code, when you write
q = "World";
Since q is a reference bound to p, this is equivalent to writing
p = "World";
Which just changes where p is pointing, not the contents of the string it's pointing at. (This also explains why it doesn't crash!)
As for your second question, references cannot be reassigned once bound to an object. If you need to have a reference that can change its referent, you should be using a pointer instead.
Hope this helps!
It is to be noted that since C++20, it is possible to change the reference held by a reference variable inside a class, using placement new, like in the following example taken from this SO post:
struct C {
int& i; // <= a reference field
void foo(const C& other) {
if ( this != &other ) {
this->~C();
new (this) C(other); // valid since C++20 even on a class
// with a reference field
}
}
};
int main() {
int a = 3, b = 5;
C c1 {.i = a};
C c2 {.i = b};
c1.foo(c2); // the inner reference field i inside c1
// was referring to a and now refers to b!
}
Code: http://coliru.stacked-crooked.com/a/4674071ea82ba31b
a) How can a reference q be reinitialized to something else?
It cannot be!
An reference variable remains an alias to which it was initialized at time of creation.
b)Isn't the string literal, p = "Hello", a constant/in read only space. So if we do,
No it doesn't.
char* &q = p;
Here q is an reference to pointer of the type char p. The string here is constant put the pointer is not, it can be pointed to another string, and the reference is alias to this pointer not the string literal so it is valid.
c) Second question I have is I have read about C++ reference type variables as they cannot be reinitialized/reassigned, since they are stored 'internally' as constant pointers. So a compiler would give a error.
int i;
int &j = i;
int k;
j = k; //This should be fine, but how we reassign to something else to make compiler flag an error
Does not reassign the reference. it changes the value of the variable to which it was alias.
In this case it changes the value of i to k
Treat reference as an alias name and I hope the world of reference will much easier to understand.
int p; // Declares p as an integer; Defines p & allocates space
int &q = p ; // Declares a Reference. Though they are symbolically 2 variables,
// they essentially refer to same name and same memory location.
So, p = 5 and q = 5 will be all the same.
In your example,
char *p = "Hello"; // Declares your pointer to "Hello". p has its own existence.
char* &q = p; // This now creates a reference (alias) to p with name q.
So all in all, p & q are names of the entity/object (memory).
So, if you assign q something, it reflects in p too. Coz it is same as the assignment to p.
So q = "World", means p too now points to "World". i.e. the Memory location which p & q both refer to - holds the address of first character of "World".
I hope the second question need not be answered if you understand the notion of reference as an alias.