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.