<< PreviousNext >>

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?

Answer and comments