Avoiding const_cast
Sometimes const is good, sometimes it is annoying.
The idea is that any attempts to modify variables marked const
should be rejected by the compiler. The way C++ handles classes
and const is that when you have a const
instance of a class,
the compiler treats all data members as if they were declared
with a const
. This effectively means that the compiler will not
allow you to modify any of the memory occupied by any of the data
members of the const object.
Sometimes, this is not what you want. For example, when you try
to implement some kind of lazy evaluation, it would be nice to
make the value
function able to operate on const
objects, since
it will not change the observable behaviour of the object itself.
To clarify, consider the following class:
class Lazy { public: Lazy() : cached(0) {} int value() { if (cached == 0) cached = heavy_computation(); return cached; } private: int cached; };
If we try to make the value
function const
, we would run into a problem
with the cached
variable. If value
is marked const, the type of the
this
pointer would be const Lazy *this
, and therefore we can think
of cached
as being declared like const int cached
. Because of this,
we can not modify cached in value
if we make it const
.
One way to solve the problem in this case would be to declare cached
as mutable
. This makes the cached
variable immune to constness
of the Lazy
class. Therefore, this would work:
class Lazy { public: Lazy() : cached(0) {} int value() const { if (cached == 0) cached = heavy_computation(); return cached; } private: mutable int cached; };
Sometimes when working with lazy loading this would not be enough, as we would have to declare everything as mutable and all updating functions as const, even though they actually modify the object (these kind of problems usually indicate that you should re-think your code, but for the fun of it, lets stick with it for now). For example:
class Lazy { public: Lazy() : loaded(false) {} int value() const { load(); return v; } int size() const { load(); return s; } protected: // load the contents of this class. virtual void loadMe() const = 0; // Values lazily loaded somehow. mutable int v, s; private: mutable bool loaded; void load() const { if (loaded) return; loadMe(); loaded = true; } }
Like I said, it feels wrong that the member function load
is declared
as const
even though it is obvious that it modifies the object. How
can we work around this? One way would of course be to use the
const_cast
to do like this:
class Lazy { public: Lazy() : loaded(false) {} int value() const { load(); return v; } int size() const { load(); return s; } protected: // load the contents of this class. virtual void loadMe() = 0; // Values lazily loaded somehow. int v, s; private: bool loaded; void load() const { if (loaded) return; Lazy *me = const_cast<Lazy*>(this); me->loadMe(); me->loaded = true; } }
Better, now we only have the load
helper declared as const. However,
we are using const_cast
, which is usually a bad sign. Is there another
way to do the same thing but avoiding the ugly const_cast
?