I just came across this code fragment, but I do not understand how it compiles:
class temp {
int value1;
mutable int value2;
public:
void fun(int val) const
{
((temp*) this)->value1 = 10;
value2 = 10;
}
};
What is the meaning of this line
((temp*) this)->value1 = 10;
value1 is getting assigned to 10, without any error. But value1 is not mutable. How does this compile?
When a member variable does not have the mutable qualifier, you cannot modify it when an object is const.
When a member variable has the mutable qualifier, you can modify it even when an object is const.
Simple example:
struct Foo
{
int var1;
mutable int var2;
};
const Foo f{};
f.var1 = 10; // Not OK
f.var2 = 20; // OK
When you have:
void fun(int val) const
{
((temp*) this)->value1 = 10;
value2 = 10;
}
you are bypassing the const-ness of the object and changing it in a way that you are not supposed to. This is subject to undefined behavior.
As far as the compiler is concerned, that code is equivalent to:
void fun(int val) const
{
temp* ptr = (temp*)this
// The compiler does not know how you got ptr.
// It is able to modify value1 through ptr since ptr
// points to a non-const object.
ptr->value1 = 10;
value2 = 10;
}
The line
((temp*) this)->value1 = 10;
is said to be "casting away const-ness". It basically tells the compiler: Take the this pointer (which has the type temp const* because we are in a const member function of temp), and pretend it were of type temp* (the subexpression (temp*)this). Then it instructs the compiler to dereference this pointer of type temp* to modify one of its members.
Casting away const-ness was how mutable data members were implemented before C++ offered the mutable keyword.
As you might have guessed, this is bad practice. It is bad practice, because it could just as well have been expressed with the mutable modifier. And it is bad practice because it uses the big gun of a C-style cast where a C++ const_cast<> would have sufficed.
The best way to learn is to start making it a trivial case and see how it goes.
a) if you comment out (*temp) and make it something like
this->value1 = 10;
error: assignment of data-member ‘temp::value1’ in read-only structure
or
b) if you comment out mutable keyword, you'll get the same error
error: assignment of data-member ‘temp::value2’ in read-only
structure
and if you read above R Sahu provided the right answer about mutable keyword.
If you are confused why can't we modify value with the normal way, remember the function is const and const is a contract that promises we are not supposed to modify or assign any values here (but mutable). so it is basically showing two techniques or hacks to break promise :-)
Related
Why does returning the reference to a pointed-to member variable work, but not the other? I know that a const member function should only return const references, but why does that not seem true for pointers?
class MyClass
{
private:
int * a;
int b;
public:
MyClass() { a = new int; }
~MyClass() { delete a; }
int & geta(void) const { return *a; } // good?
int & getb(void) const { return b; } // obviously bad
};
int main(void)
{
MyClass m;
m.geta() = 5; //works????
m.getb() = 7; //doesn't compile
return 0;
}
int & geta(void) const { return *a; } // good?
int & getb(void) const { return b; } // obviously bad
In a const-function, every data member becomes const in such way that it cannot be modified. int becomes const int, int * becomes int * const, and so on.
Since the type of a in your first function becomes int * const, as opposed to const int *, so you can change the data (which is modifiable):
m.geta() = 5; //works, as the data is modifiable
Difference between : const int* and int * const.
const int* means the pointer is non-const, but the data the pointer points to is const.
int * const means the pointer is const, but the data the pointer points to is non-const.
Your second function tries to return const int &, since the type of b become const int. But you've mentioned the actual return type in your code as int &, so this function would not even compile (see this), irrespective of what you do in main(), because the return type doesn't match. Here is the fix:
const int & getb(void) const { return b; }
Now it compiles fine!.
Because a becomes int * const a;. That is, you cannot change the value of a (change what it points at), just as const says. The const-ness of what a points at is a completely different issue.
Please see my answer here for a thorough discussion of const and const member functions.
Nawaz's answer is very good. However, the point about the compiler catching the error need not always hold. The C++ FAQ warns about it here.
The good news is that the compiler will often catch you if you get this wrong. In particular, if you accidentally return a member of your this object by non-const reference [...] the compiler will often detect it and give you a compile-time error [...].
The bad news is that the compiler won’t always catch you: there are some cases where the compiler simply won’t ever give you a compile-time error message.
Translation: you need to think. If that scares you, find another line of work; “think” is not a four-letter word.
Would it be undefined behavior to change where a pointer points, when its data is const? Example:
const char* p = "foo";
p = "boo";
I believe that this is not UB, because the pointer itself is not const and I'm not modifying the "foo" object.
Extra question: and altering not const data of a const pointer? Would be UB? Example:
char* const p = "foo";
(*(char**)&p) = (char*)malloc(strlen(p));
I believe that this is not UB, because the pointer itself is not const and I'm not modifying the "foo" object.
This is correct. The pointer is not const so you can change it to point to something else if you want. In this case it won't cause a meory leak but remember that if the pointer points to data allocated with new and it is the only pointer to that data then you need to call delete before reassigning the pointer otherwise you'll have a meory leak.
Extra question: and removing the constness of pointer? Would be UB?
It is only UB if you try to modify a const object you removed const from, which you do in this case. Just removing const is okay, and sometimes needed, but you are never allowed to modify the object unless it was not const to begin with. For example the following is legal since foo is not const.
int foo = 42;
void bar(int const& baz) { const_cast<int&>(baz) = 21; }
int main()
{
bar(foo);
}
on the other hand
const int foo = 42;
void bar(int const& baz) { const_cast<int&>(baz) = 21; }
int main()
{
bar(foo);
}
is not legal as foo is const
The code in the first snippet is 100% correct. You have a pointer to const p, which you repoint to something else. All good and in mint condition.
The second piece of code is ill-formed. You can't modify an object after removing the constness, if original object was const-qualified (which string literal is).
Would it be undefined behavior to change where a pointer points, when its data is const? Example:
const char* p = "foo";
p = "boo";
I believe that this is not UB, because the pointer itself is not const and I'm not modifying the "foo" object.
Extra question: and altering not const data of a const pointer? Would be UB? Example:
char* const p = "foo";
(*(char**)&p) = (char*)malloc(strlen(p));
I believe that this is not UB, because the pointer itself is not const and I'm not modifying the "foo" object.
This is correct. The pointer is not const so you can change it to point to something else if you want. In this case it won't cause a meory leak but remember that if the pointer points to data allocated with new and it is the only pointer to that data then you need to call delete before reassigning the pointer otherwise you'll have a meory leak.
Extra question: and removing the constness of pointer? Would be UB?
It is only UB if you try to modify a const object you removed const from, which you do in this case. Just removing const is okay, and sometimes needed, but you are never allowed to modify the object unless it was not const to begin with. For example the following is legal since foo is not const.
int foo = 42;
void bar(int const& baz) { const_cast<int&>(baz) = 21; }
int main()
{
bar(foo);
}
on the other hand
const int foo = 42;
void bar(int const& baz) { const_cast<int&>(baz) = 21; }
int main()
{
bar(foo);
}
is not legal as foo is const
The code in the first snippet is 100% correct. You have a pointer to const p, which you repoint to something else. All good and in mint condition.
The second piece of code is ill-formed. You can't modify an object after removing the constness, if original object was const-qualified (which string literal is).
Consider the following code:
int * p;
void Set()
{
int i = 7;
p = & i; // potentially disastrous
}
I know that pointing to a value that may be overwritten is bad but I recall reading that there was a way to prevent a value such as i from being lost when declared and then pointed to in this way. I've scoured my text book but have had trouble finding the exact text elaborating on this and am starting to think I may have even imagined it. Is there a way of safely pointing to a temporary value declared inside of a function? A way of making the temporary value i no longer temporary?
Thanks for all your help in advance!
I recall reading that there was a way to prevent a value such as i from being lost when declared and then pointed to in this way
The closest thing to what you mention (if I understood your sentence correctly) is the rule which allows prolonging the lifetime of a temporary when bound to a reference (§ 12.2/5 of the C++11 Standard):
struct X { int x = 0; };
X foo() { return X(); }
int main()
{
X const& x = foo(); // The lifetime of the object bound to x is prolonged
x.x = 42; // This is OK
}
However, there is no such rule for pointers nor for objects that are not temporaries.
A way of making the temporary value i no longer temporary?
In your example, i is not a temporary: it is an object with automatic storage duration. A temporary is an object whose life-time (usually) terminates at the end of the full expression that creates it (with the exception mentioned above and a few more listed in § 12.2/4-5).
taking const ref to an temporary extends liifetime of this temporary up to the end of scope of this const reference, however in your example you are not taking address of temporary but local automatic object i.
so to extends lifetime of temporary do simply:
const int& iref=int(3);
int a= iref;
//..do stuff with iref, i.e:
printf("iref: %d\n", a);
output:
iref: 3
RUN SUCCESSFUL (total time: 46ms)
The value of i has to reside in memory somewhere. If you declare it inside a function, it is likely to be allocated on the stack and will be lost when the function returns. If you really need the value to always be constant like in your example, then what you need is to declare it outside the function as static const:
int * p;
static const int i = 7;
void Set()
{
p = & i;
}
If this is not what you need, maybe you can elaborate a bit on your very minimal example code so that we can better understand what you are trying to achieve.
you can use the static keyword.
#include <stdio.h>
int *p;
void set() {
static int x = 7;
p = &x;
}
int main(){
set();
printf("%d", *p);
}
correctly prints 7.
however, I would strongly discourage using such a piece of code in anything but a research on the static keyword and pointers.
struct A {
int &r;
A (int &i) : r(i) {}
void foo () const {
r = 5; // <--- ok
}
};
The compiler doesn't generate any error at r = 5;.
Does it mean that &r is already const-correct being a reference (logical equivalent of int* const) ? [Here is one related question.]
I'm not sure exactly what you mean by "already const-correct", but:
Assigning to r is the same as assigning to whatever thing was passed into the constructor of A. You're not modifying anything in the instance of A when you do this, so the fact that foo is declared const isn't an obstacle. It's very much as if you'd done this:
struct A {
int * r;
A (int * i) : r(i) {}
void foo () const { *r = 5; }
}
The fact that foo is const means that it doesn't modify anything in the A instance it's called on. There's no conflict between that and having it modify other data it was supplied with.
Of course if you happened to arrange for r to be a reference to some member of A then calling foo would modify the instance of A after all. The compiler can't catch all possible ways in which constness of a member function might be violated; when you declare a member function const you're promising that it doesn't engage in any such subterfuge.
Yes, it's the logical equivalent of int* const.
You may want to create and use appropriately qualified accessors in this case to prevent unwanted alterations to the value r references.
I interpret a const member function as implicitly inserting const just to the left of every data member that doesn't already have such a qualifier. That const is already there implicitly for references (int & const r; is illegal syntax). In other words, references are "already const-correct" to use your nomenclature.
It would be nice if the const qualifier on a member function had the affect of inserting const in every possible valid position for every data member (e.g., data member int ** foo; acts like int const * const * const foo; in a const member function), but that isn't what happens, and it isn't what the standard says will happen.