Domanda

I'm using C++ concepts to ask the question because that's where it came up, but I'm asking about any language feature or tool that would somehow support this.

Suppose I have a class that's a light wrapper around a list of values. These values are expensive to compute, so they're computed as they're asked for. The object has all the information it needs to compute the values at construction time.

objectInstance.get(5) will always return 10, but the get method cannot be const because it will add a value to the internal list the first time it's called.

What support is there for such "effectively-const" methods?

È stato utile?

Soluzione

Effectively what you’re looking for is a way to present an entity as immutable through its public API but still allow mutable state as an implementation detail behind the scenes.

Sometimes that’s a valid design choice, but do not use is lightly. Especially – since you promise the caller immutability – make absolutely sure that after the entity is created it does not change its publicly visible state ever.

I can think of two situations where such hidden mutability is a valid approach.

  • For lazy evaluation like in your own example.
  • For internal state that needs to be mutable for administrative/housekeeping purposes. A straight forward example is acquiring and releasing a mutex (i.e. changing its state) to read a variable in a thread-safe manner.

I can’t give you an overview over how “internal mutability” is supported in a lot of languages because I mostly know C++ and Python, and I’m starting to dabble a bit in Rust.

In Python constness in the sense of your question does not exist at all, so special support for internal mutability is not needed.

In Rust Cell and RefCell seem to be the things to looks for.

In C++ the language provides the mutable keyword (and sometimes an argument can be made for const_cast):

class Entity
{
public:
    explicit Entity(int i): m_i_might_be_const(i) {}

    int get_mutable() const
    {
        ++m_i_can_change;
        return m_i_can_change;
    }

    int get()
    {
        ++m_i_might_be_const;
        return m_i_might_be_const;
    }

    // Fails to compile with a message like:
    // "Const member function cannot change object’s state."
    int get_error() const
    {
        ++m_i_might_be_const;
        return m_i_might_be_const;
    }

private:
    mutable int m_i_can_change = 0;
    int m_i_might_be_const;
}


int main()
{
    const Entity e(42);

    // Works because m_i_can_change is mutable
    auto a = e.get_mutable();

    // Fails to compile with a message like:
    // "Calling non-const member function on const object e."
    auto b = e.get();
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
scroll top