C++ standard says that modifying an object originally declared const
is undefined behavior. But then how do constructors and destructors operate?
class Class {
public:
Class() { Change(); }
~Class() { Change(); }
void Change() { data = 0; }
private:
int data;
};
//later:
const Class object;
//object.Change(); - won't compile
const_cast<Class&>( object ).Change();// compiles, but it's undefined behavior
I mean here the constructor and destructor do exactly the same thing as the calling code, but they are allowed to change the object and the caller is not allowed - he runs into undefined behavior.
How is it supposed to work under an implementation and according to the standard?
To elaborate on what Jerry Coffin said: the standard makes accessing a const object undefined, only if that access occurs during the object's lifetime.
7.1.5.1/4:
The object's lifetime begins only after the constructor has finished.
3.8/1:
The standard doesn't really say a lot about how the implementation makes it work, but the basic idea is pretty simple: the
const
applies to the object, not (necessarily) to the memory in which the object is stored. Since the ctor is part of what creates the object, it's not really an object until (sometime shortly after) the ctor returns. Likewise, since the dtor takes part in destroying the object, it's no longer really operating on a complete object either.The standard explicitly allows constructors and destructors to deal with
const
objects. from 12.1/4 "Constructors":And 12.4/2 "Destructors":
As background, Stroustrup says in "Design and Evolution of C++" (13.3.2 Refinement of the Defintion of
const
):Constness for a user-defined type is different than constness for a built-in type. Constness when used with user-defined types is said to be "logical constness." The compiler enforces that only member functions declared "const" can be called on a const object (or pointer, or reference). The compiler cannot allocate the object in some read-only memory area, because non-const member functions must be able to modify the object's state (and even const member functions must be able to when a member variable is declared
mutable
).For built-in types, I believe the compiler is allowed to allocate the object in read-only memory (if the platform supports such a thing). Thus casting away the const and modifying the variable could result in a run-time memory protection fault.
Here's a way that ignoring the standard could lead to incorrect behavior. Consider a situation like this:
If optimized, the compiler knows that v is const so could replace the call to
v.get()
with5
.But let's say in a different translation unit, you've defined
cheat()
like this:So while on most platforms this will run, the behavior could change depending on what the optimizer does.