Any attempt to modify a const
object results in undefined behavior. This applies to const
variables, members of const
objects, and class members declared const
. (However, a mutable
member of a const
object is not const
.)
Such an attempt can be made through const_cast
:
const int x = 123;
const_cast<int&>(x) = 456;
std::cout << x << '\n';
A compiler will usually inline the value of a const int
object, so it's possible that this code compiles and prints 123
. Compilers can also place const
objects' values in read-only memory, so a segmentation fault may occur. In any case, the behavior is undefined and the program might do anything.
The following program conceals a far more subtle error:
#include <iostream>
class Foo* instance;
class Foo {
public:
int get_x() const { return m_x; }
void set_x(int x) { m_x = x; }
private:
Foo(int x, Foo*& this_ref): m_x(x) {
this_ref = this;
}
int m_x;
friend const Foo& getFoo();
};
const Foo& getFoo() {
static const Foo foo(123, instance);
return foo;
}
void do_evil(int x) {
instance->set_x(x);
}
int main() {
const Foo& foo = getFoo();
do_evil(456);
std::cout << foo.get_x() << '\n';
}
In this code, getFoo
creates a singleton of type const Foo
and its member m_x
is initialized to 123
. Then do_evil
is called and the value of foo.m_x
is apparently changed to 456. What went wrong?
Despite its name, do_evil
does nothing particularly evil; all it does is call a setter through a Foo*
. But that pointer points to a const Foo
object even though const_cast
was not used. This pointer was obtained through Foo
's constructor. A const
object does not become const
until its initialization is complete, so this
has type Foo*
, not const Foo*
, within the constructor.
Therefore, undefined behavior occurs even though there are no obviously dangerous constructs in this program.