I’m a programmer who strives to write const-correct code. I’ll admit that it can be challenging, but once you get used to doing it, your code is generally cleaner and more maintainable. This happens at the expense of flexibility. When you say “this is a const class method”, you are no longer able to mutate the underlying object. This is a good thing, of course, because the caller may be using a const object which cannot be modified! But what if you really do want to mutate the underlying object?
This might seem like sacrilege, but it actually has a sensible basis in reality. In C++, the “const” modifier doesn’t mean the object is immutable. It means that it must appear to be immutable to an outside observer. That’s a fine distinction, but an important one. Let’s take an example to demonstrate:
class ThingPool { public: Thing *Find( const std::string& name ) const; }; class ThingProvider { private: ThingPool *mPoolOfThings; public: Thing *FindThing( const std::string& name ) const { return mPoolOfThings->Find( name ); } };
We have a class which provides “Thing” objects, and it has a const-correct method which performs a lookup on the stored provider object. So far, so good — we’re doing “the right thing.” But then we realize that the act of looking up Things from the provider is a tad slower than we’d like. After doing some measuring and analysis, we realize that the majority of the time could be cut out easily by caching the last Thing accessed.
After adding a simple caching mechanism, our code looks like this:
// This won't compile! class ThingProvider { private: // Cache the last thing we access, because getting // Things can be expensive Thing *mLastThingAccessed; std::string mLastThingName; ThingPool *mPoolOfThings; public: Thing *FindThing( const std::string& name ) const { if (name != mLastThingName) { // This is a new lookup operation, so find it and // cache it in case the user wants it later mLastThingAccessed = mPoolOfThings->Find( name ); mLastThingName = name; } return mLastThingAccessed; } };
However, this code won’t compile. FindThing is a const method, and yet it’s trying to mutate the underlying object by assigning to cache variables.
You might be tempted to remove the const-ness of the FindThing method. After all, you need to change the underlying object, so that means it’s not const. However, const-correctness isn’t just about object mutability; it’s about apparent object mutability. To the outside observer, caching values from FindThing doesn’t change the observable state of the ThingProvider class.
The way to keep the const-correctness is to use the mutable keyword. This is a storage class specifier, like extern or static and it nullifies the const specifier of the containing class instance, permitting the mutable members to be modified within a const method.
class ThingProvider { private: // Cache the last thing we access, because getting // Things can be expensive mutable Thing *mLastThingAccessed; mutable std::string mLastThingName; ThingPool *mPoolOfThings; public: Thing *FindThing( const std::string& name ) const { if (name != mLastThingName) { // This is a new lookup operation, so find it and // cache it in case the user wants it later mLastThingAccessed = mPoolOfThings->Find( name ); mLastThingName = name; } return mLastThingAccessed; } };
Now you’ve adhered to the contract the user expects — FindThing is still visibly constant. Yet you were able to implement the caching mechanism to enhance performance.
The mutable keyword is a very powerful tool to have in your arsenal, but it’s not one you should whip out often. If you find yourself reaching for mutable, ask yourself “is this data observable from outside of the class in any fashion?” If the answer is “yes”, then you’ve got some more thinking to do. Here’s an example of a case when the mutable keyword is being abused:
class Thing { private: // Don't do this, it's an abuse of mutable! mutable void *mData; mutable size_t mSize; public: const void *GetData() const { return mData; } size_t GetSize() const { return mSize; } void ClaimOwnershipOfData( void **outData, size_t *outSize ) const { if (outData) *outData = mData; if (outSize) *outSize = mSize; // These require the mutable keyword because the // function call is const mData = NULL; mSize = 0; } };
While this code compiles, it is an abuse of the mutable keyword. Should ClaimOwnershipOfData be const? That’s debatable. But given the implementation of the Thing class, it should be clear that mData and mSize should not be mutable. They’re exposed via the two getter functions, which means that mutating them from a const method would be externally visible. This is obviously a breach of contract.
tl;dr: you can use the mutable storage specifier to allow you to mutate data from within a const function. This allows you to keep the appearance of immutability while letting you mutate internal implementation details.