When can the value change when removing const from const_cast?
In accordance with ยง7.1. 5.1 / 4:
Except that any member of a class declared mutable (7.1.1) is mutable, any attempt to modify a const object during its lifetime (3.8) results in undefined behavior.
So my question becomes: when is an object a const object?
Specifically, is a const member in a non-const object treated as a const object?
class Foo {
const Bar bar;
void replaceBar(Bar bar2) {
*(const_cast<Bar *>&bar) = bar2; // Undefined behavior?
}
}
This is because I have an immutable class (all fields are constants), but I want to have a move constructor that technically modifies the passed value. I'm fine with the "trick" in this case as it doesn't violate the boolean constant.
source to share
A simple rule of thumb is: you can drop a constant if the original object is not a constant. So if you have a non-cont object and, say, you pass a const reference to it in a function, it is legal to drop the constant in the function. In your example, the original object is const, so the casting constant is undefined behavior.
source to share
Let's make it a complete example:
struct Bar { int x; };
struct Foo {
const Bar bar;
Foo( int x ):bar(x) {}
void replaceBar(Bar bar2) {
*(const_cast<Bar *>&bar) = bar2; // Undefined behavior?
}
};
now, let's break the world.
int main() {
Foo f(3);
Bar b = {2};
f.replaceBar(b);
std::cout << f.bar.x << "\n";
}
the above can and probably should output 3 because the const
object Bar
was created with x=3
. The compiler can and should assume that the object const
will not change throughout its lifetime.
Let's break the world more:
struct Bar {
int* x;
Bar(int * p):x(p) {}
~Bar(){ if (x) delete x; }
Bar(Bar&& o):x(o.x){o.x=nullptr;}
Bar& operator=(Bar&& o){
if (x) delete x;
x = o.x;
o.x = nullptr;
}
Bar(Bar const&)=delete;
Bar& operator=(Bar const&)=delete;
};
struct Foo {
const Bar bar;
Foo( int* x ):bar(x) {}
void replaceBar(Bar bar2) {
*(const_cast<Bar *>&bar) = bar2; // Undefined behavior?
}
};
now the same game can cause the compiler to delete something twice.
int main() {
int* p1 = new int(3);
Foo f( p1 );
Bar b( new int(2) );
f.replaceBar(std::move(b));
}
and the compiler will remove p1
once inside replaceBar
and must remove it also at the end main
. He can do this because you guaranteed to f.bar.x
remain unchanged ( const
) until the end of his scope, then you broke that promise in replaceBar
.
Now, this is just what makes sense to the compiler: the compiler can literally do anything after you've changed the object declared const
, since you've invoked undefined behavior, Nasal daemons, time travel are all to capture.
Compilers take advantage of the fact that some behavior is undefined (aka, not allowed) for optimization.
source to share