In C++ it is possible to allocate a const object on heap:
const Class* object = new const Class();
const_cast<Class*>( object )->NonConstMethod(); // UB
so that attempt to write into an object will be UB.
I don’t get how such an object will be different from a heap-allocated object that is not declared const:
const Class* object = new Class();
I mean when I allocate an object on stack it goes to automatic storage which is implementation-specific and so there might be some implementation-specific means that would allow allocating const objects in some special way that would yield UB when I write to an object.
Yet whenever I use new the compiler is required to emit operator new() function invokation and that function can’t possibly do anything different – it just allocates memory in a uniform manner regardless of whether there was const in my code.
How is a const heap-allocated object different from a non-const one and how is undefined behavior possible if I try to modify it?
There is no difference in the object. There is a difference in the (compile-time) type of the variable(s) used to refer to the memory area.
This is semantic friction only: the variable is different, the actual memory used by the data bits is const/volatile agnostic.
For a very amusing and enlightening story describing similar semantic friction see this all-time-favourite answer by Eric Lippert:
On the Undefined Behaviour
Treating const data in a non-const way can lead to Undefined Behaviour, because the compiler is allowed to do certain optimizations based on the knowledge that a const variable won’t change1. Changing it none-the-less (e.g. by
const_cast<>) can lead to erronous results because the compiler’s assumptions are actively negated.1 Note that
volatileis there to help in cases where const variables can get modified concurrently. You can see howconstis a ‘local’ won’t/can’t touch promis, whereasvolatilesays: ‘don’t assume this won’t change, even though it is not being written to in this code segment’.`