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?