I have this piece of code:
const int x = 7;
const int &y = x;
int *z = (int*)&y;
*z = 8;
cout << x<< endl;
cout << y<< endl;
cout << *z<< endl;
Which produces the following output:
7
8
8
I know that one should not remove the const as I did above. However, why does x print out 7? I've only created a single var, so if the output shows 7 and 8, then then there should be 2 vars created. How come there be 2 different numbers printed.
I know that one should not remove the const as I did above.
Right. Doing so is undefined behaviour, which means anything can happen. (More precisely, creating z is not undefined behaviour; modifying x through it is.)
However, why does x print out 7? I've only created a single var, so if the output shows 7 and 8, then then there should be 2 vars created. How come there be 2 different numbers printed.
Reasoning about undefined behaviour is weird - but it can be useful to understand what is happening for debugging purposes. It this case, what has almost certainly happened, is that the compiler has optimized:
cout << x<< endl;
to
cout << 7 << endl;
because there is no way you can legitimately modify the value of x. You then illegitimately modify the value of x, and manage to print out the new value.
Important Note
Do not attempt to rely on this behaviour; as I said above, it is only useful for debugging purposes. If the compiler changes it might get better at optimization and just print 7 for all values (because *z = 8 is undefined behaviour), or it might put x in read-only memory (and cause a access violation), or it might end up printing 8 for all values, or anything else might happen.
However, why does x print out 7?
Modifying a const object has undefined behaviour.
Casting away const-ness and using the pointer to modify a const object has undefined behavior. You are seeing the result of that.
The standard even has almost the same example you posted.
[dcl.type.cv/4]
Except that any class member declared mutable can be modified, any
attempt to modify a const object during its lifetime results in
undefined behavior. [ Example:
const int ci = 3; // cv-qualified (initialized as required)
ci = 4; // ill-formed: attempt to modify const
int i = 2; // not cv-qualified
const int* cip; // pointer to const int
cip = &i; // OK: cv-qualified access path to unqualified
*cip = 4; // ill-formed: attempt to modify through ptr to const
int* ip;
ip = const_cast<int*>(cip); // cast needed to convert const int* to int*
*ip = 4; // defined: *ip points to i, a non-const object
const int* ciq = new const int (3); // initialized as required
int* iq = const_cast<int*>(ciq); // cast required
*iq = 4; // undefined: modifies a const object
For another example,
struct X {
mutable int i;
int j;
};
struct Y {
X x;
Y();
};
const Y y;
y.x.i++; // well-formed: mutable member can be modified
y.x.j++; // ill-formed: const-qualified member modified
Y* p = const_cast<Y*>(&y); // cast away const-ness of y
p->x.i = 99; // well-formed: mutable member can be modified
p->x.j = 99; // undefined: modifies a const member
— end example ]
Related
I am reading through Stroustrup's 4th edition : The C++ Programming Language. I have a python/java background so the first 4 chapters are fine so far.
In Chapter 3 I saw:
complex& operator+=(complex z) { re+=z.re , im+=z.im; return ∗this; }
That began a day long attempt to write this question:
First I figured out that it is returning a reference to the object and not a copy. As I was able to confirm in this question.
And I was able to understand the difference between returning a reference into a reference variable vs. a regular variable from this question
And I did my own trial
class Test {
public:
Test():x{5}{}
int x;
void setX(int a) {x = a;}
Test& operator+=(Test z) {x+=z.x; return *this;}
// the keyword this is a pointer
Test* getTest() {return this;}
// but I can return the reference by *this
Test& getTest1() {return *this;}
// or I can return a copy
Test getTest2() {return *this;}
};
That lead me to question why it is called de-reference, so I did this trial
int x = 8;
int* p = &x;
int y = *p;
int& z = *p;
x++; // let's have some fun
std::cout << y << std::endl;
std::cout << z << std::endl;
As expected y = 8 and z = 9, so how did the de-reference return the address in one case, and the value in the other? More importantly how is C++ making that distinction?
It's exactly like in your Test class functions.
int y = *p;
int& z = *p;
y is a copy of what p points to.
z is a reference to (not an address) what p points to. So changing z changes *p and vice-versa. But changing y has no effect on *p.
As expected y = 8 and z = 9, so how did the de-reference return the address in one case, and the value in the other? More importantly how is C++ making that distinction?
The de-reference returned the actual thing referenced in both cases. So there is no distinction for C++ to make. The difference is in what was done with the result of the dereference.
If you do int j = <something>; then the result of the something is used to initialize j. Since j is an integer, the <something> must be an integer value.
If you do int &j = <something>; then the result of the something is still used to initialize j. But now, j is a reference to an integer, and the <something> must be an integer, not just an integer value.
So, what *this does is the same in both cases. How you use a value doesn't affect how that value is computed. But how you use it does affect what happens when you use it. And these two pieces of code use the dereferenced object differently. In one case, its value is taken. In the other case, a reference is bound to it.
It's possible to consider a pointer int* p as pointing to an address where data of type int resides. When you de-reference this, the system retrieves the value at that memory address (the address is the actual value of p itself). In the case of int y = *p; you put a copy of that int value on the stack as the locator value y.
On the other hand, de-referencing on the left-hand side in *p = 13; means you are replacing the int value *p stored at the memory address denoted by the value of p with the right-hand-side value 13.
The reference lvalue int& z in int& z = *p; is not a copy of the int value pointed to by p but rather a left-hand side reference to whatever is at the particular memory address returned by *p (i.e. the actual value held by p itself).
This doesn't mean much difference in your contrived case, but e.g. given a Foo class with a Foo::incrementCount() value,
Foo* p = new Foo();
p->incrementCount();
Foo& ref = *p;
ref.incrementCount();
The same method for the same instance will be called twice. In contrast, Foo foo = *p will actually copy the entire Foo instance, creating a separate copy on the stack. Thus, calling foo.incrementValue() won't affect the separate object still pointed to by p.
The following code compiles fine. However I wonder if it is legal C++. So more specific, if I have a const object, am I allowed to modify variables through pointers/references of that object?
class Foo {
public:
int* a;
int& b;
Foo(int* _a, int& _b) : a(_a), b(_b) {}
};
int main ( int argc, char* argv[] ) {
int x = 7;
const Foo bar(&x, x);
*bar.a = 3; //Leagal?
bar.b = 7; //Legal?
return 0;
}
It's legal, as const-ness of the class means that the class member is constant. a is a pointer, so the address the pointer points to is constant, but the value stored at that address need not be.
Hence bar.a is effectively an int * const, not an int const *.
As, after initialization, a reference cannot be made to refer to another entity anyway, it does not matter for bar.b whether bar is declared const or not.
The constant variant of a pointer is a constant pointer, not a pointer to a constant. The constant variant of a reference is a reference, not a reference to a constant.
Small digression: You should be careful with references as members anyway in connection with const-ness, as the following will probably compile
struct Y { int m_a; };
struct X {
const Y & m_y;
X (const Y & y) : m_y (y) { }
};
Y y;
y.m_a = 1;
X x (y); // or const X x (y) -- does not matter
// X.m_y.m_a == 1
y.m_a = 2;
// now X.m_y.m_a == 2, although X.m_y is supposed to be const
As it is possible to assign a pointer to non-const to a pointer to const, you can build an analogous example with pointers. Remember that const does only guarantee that YOU will not modify a variable via this very variable, it cannot guarantee that the contents of the variable are not modified at all.
I find the best way to think about const in C++ is that it protects the physical bits of the object. It has no real protection for objects that it refers to. You can control the object definition with const methods to provide deeper protection of values but by default C++ really only protects the physical object itself
One of the reasons why C++ allows you to modify the contents of a pointer through a const value is that it really can't stop you. Imagine for a second that C++ disallowed that particular construct. You could trivially violate it by doing the following
size_t t1 = (size_t)bar.a; // legal
int* t2 = (int*)t1; // legal
*t2 = 3; // exactly the same as *bar.a = 3
For example, if F is a reference to an integer, where the reference is not permitted to be pointed to a new object once it is initially pointed to one.
Can I write to declaration like: const int & F?
I am confused about reference and pointer, because they both represent the address of something, but we always write parameter use reference as: const & F, I understand that this is to reduce the copy and does not allow others to change it, but are there any other meanings? and why do we need "const" after a function declaration like: int F(int z) const; this const makes the return type const or everything in the function const?
One more example,
void F(int* p)
{
p+=3;
}
int z=8;
F(&z);
std::cout<<z<<std::endl;
What is the output for z since z is a reference, and I pass it as a pointer who points to an integer.Increasing p by 3 just makes the address different and does not change its value?
Just a first pass at some answers - if anything is unclear please comment and I'll try to elaborate.
int a = 3;
declares an integer, a, with the initial value 3, but you are allowed to change it. For example, later you can do
a = 5; // (*)
and a will have the value 5. If you want to prevent this, you can instead write
const int a = 3;
which will make the assignment (*) illegal - the compiler will issue an error.
If you create a reference to an integer, you are basically creating an alias:
int& b = a;
, despite appearances, does not create a new integer b. Instead, it declares b as an alias for a. If a had the value 3 before, so will b, if you write b = 6 and print the value of a, you will get 6 as well. Just as for a, you can make the assignment b = 6 illegal by declaring it as const:
const int& b = a;
means that b is still an alias for a, but it will not be used to assign a different value to a. It will only be used to read the value of a. Note that a itself still may or may not be constant - if you declared it as non-const you can still write a = 6 and b will also be 6.
As for the question about the pointers: the snippet
void F(int* p) {
p += 3;
}
int z = 8;
F(&z);
does not do what you expected. You pass the address of z into the function F, so inside F, the pointer p will point to z. However, what you are doing then, is adding 3 to the value of p, i.e. to the address that p points to. So you will change to pointer to point at some (semi)random memory address. Luckily, it's just a copy, and it will be discarded. What you probably wanted to do, is increment the value of the integer that p points to, which would be *p += 3. You could have prevented this mistake by making the argument a int* const, meaning: the value of p (i.e. address pointed to) cannot be changed, but the value it points to (i.e. the value of z, in this case) can. This would have made *p += 3 legal but not the "erroneous" (unintended) p += 3. Other versions would be const int* p which would make p += 3 legal but not *p += 3, and const int* const` which would have allowed neither.
Actually, the way you have written F is dangerous: suppose that you expand the function and later you write (correctly) *p += 3. You think that you are updating the value of z whose address you passed in, while actually you are updating the value of a more-or-less random memory address. In fact, when I tried compiling the following:
// WARNING WARNING WARNING
// DANGEROUS CODE - This will probably produce a segfault - don't run it!
void F(int* p) {
p += 3; // I thought I wrote *p += 3
// ... Lots of other code in between, I forgot I accidentally changed p
*p += 3; // NOOOOOOOOOOO!
}
int main()
{
int z=8;
F(&z);
std::cout << z;
return 0;
}
I got a segmentation fault, because I'm writing at an address where I haven't allocated a variable (for all I know I could have just screwed up my boot sector).
Finally, about const after a function declaration: it makes the this pointer a const pointer - basically the compiler emits const A* this instead of just A* this. Conceptually, it states your intention that the function will not change the state of the class, which usually means it won't change any of the (internal) variables. For example, it would make the following code illegal:
class A {
int a;
void f() const {
a = 3; // f is const, so it cannot change a!
}
};
A a;
a.f();
Of course, if the function returns something, this value can have its own type, for example
void f();
int f();
int& f();
const int f();
const int& f();
are functions that return nothing, a (copy of) an integer, a (reference to) an integer, a constant (copy of) an integer, and a constant reference of an integer. If in addition f is guaranteed not to change any class fields, you can also add const after the brackets:
void f() const;
int f() const;
int& f() const;
const int f() const;
const int& f() const;
The way I remember the difference between references and pointers is that a reference must exist and the reference cannot change.
A pointer can be changed, and usually needs to be checked against NULL or tested to verify it points to a valid object.
Also, an object passed by reference can be treated syntactically like it was declared in the function. Pointers must use deferencing syntax.
Hope that helps.
You are confusing things.
First of all int z=8; F(&z); here z IS NOT a reference.
So let me start with the basics:
when found in a type declaration the symbol & denotes a reference, but in any other context, the symbol & means address of.
Similar, in a type declaration * has the meaning of declaring a pointer, anywhere else it it the dereferencing operator, denoting you use the value at an address.
For instance:
int *p : p is a pointer of type int.
x = *p : x is assigned the value found at address p.
int &r = a : r is reference of type int, and r refers the variable a.
p = &a : p is assigned the address of variable a.
Another question you have: the const at the end of a function, like int f(int x) const. This can be used only on non-static class methods and specifies that the function does not modify the object. It has nothing to do with the return value.
I am reading about references in C++. It says that int& a = 5 gives compile time error.
In Thinking in C++ - Bruce Eckel, author says that compiler must first allocate the storage for an int and produce the address to bind to the reference. The storage must be const because changing it would make no sense.
I am confused at this point. I am not able to understand the logic behind it. Why can't be change the content in the storage? I understand that it's invalid as per C++ rules, but why?
"The storage must be const because changing it would make no sense."
If you want a be a reference to a const value, you must declare it as const, because a is referencing to a temporary constant value, and changing it is not possible.
const int &a = 123;
a = 1000; // `a` is referencing to temporary 123, it is not possible to change it
// We can not change 123 to 1000
// Infact, we can change a variable which its value is 123 to 1000
// Here `a` is not a normal variable, it's a reference to a const
// Generally, `int &a` can not bind to a temporary object
For non-const bindings:
int x = 1;
int &a = x;
a is a reference to a lvalue. Simple speaking, it's an alias name for another variable, so on the right hand you should give a variable. The reference a can not change and bind to another variable after it's first binding;
In C++11, you can reference to temporary objects/values by rvalue references:
int &&a = 123;
int& a = 5;
In order for the above code to work, int& needs to bind to a temporary object of type int created out of the expression 5. But binding int& to a temporay didn't appeal to Bjarne Stroustrup — and he gave an example, similar to the following, to illustrate his point:
void f(int &i) { ++i; }
float x = 10.0;
f(x);
std::cout << x <<< std::endl;
What will the std::cout print1? Looks like it will print 11.
It feels, ++i is changing the argument x, but it doesn't. This is one reason why the creator of C++ didn't permit temporaries to bind to non-const reference.
However, you can do this:
int const & i = 10;
int const & j = x; //x is float
And since C++11, you can do this:
int && i = 10;
int && i = x; //x is float
Hope that helps.
1. assuming int& can bind to the temporary created out of x.
What you can do is
int b=5;
int &a=b;
or
const int& a = 5;
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.