Trending
Opinion: How will Project 2025 impact game developers?
The Heritage Foundation's manifesto for the possible next administration could do great harm to many, including large portions of the game development community.
In this post I take a look at how to use "mutable" in C++ while giving in to the urge to over-comment some code.
// Ever wondered what the keyword "mutable" is for? Been annoyed by duplicate
// functions while trying to write const-correct code? This might be what you
// need...
class MyContainer {
public:
// Suppose we've got a container class that holds some objects...
struct MyObject {};
private:
// The objects could be stored in a regular array, like this.
MyObject contents[10];
// Or, you can declare the contents mutable. This means the array is never
// treated as a const member, even if "this" is a const pointer (such as
// inside a const member function)
mutable MyObject mutableContents[10];
public:
// For example, this lookup() method is declared const, and it is const
// in the sense that it doesn't change the container. However, it returns
// objects from the container as non-const, so the objects *can* be
// changed...
MyObject& lookup( size_t idx ) const {
// Illegal! All members are treated as const in a const method, and
// you can't convert from const to non-const to return it.
//return contents[idx];
// but mutable is okay...
return mutableContents[idx];
}
// Does this violate what it means for the container to be const?
//
// That's up to you and the design of your class. Maybe it doesn't matter
// to the const-ness of the container what the values of the objects are.
// Usually however, we want to be more strict...
// If you don't want the contents of a const container to be changeable,
// you could implement the lookups as a pair of functions like
// strictLookup(), below.
MyObject& strictLookup( size_t idx ) {
return lookup(idx);
}
// What's nice about this approach is that you don't have two copies of
// the lookup code (which in this example is trivial, but wouldn't be in
// a real container). Both the const and non-const versions of
// strictLookup() can share lookup(), because it is a const method
// accessing a mutable data member.
//
// BTW - A properly strict version of this class would also make lookup()
// private.
const MyObject& strictLookup( size_t idx ) const {
return lookup(idx);
}
// The catch with using mutable is that the compiler won't stop *you*
// from changing the mutable members of your class inside your const
// methods, which you probably don't want to do. So using mutable
// requires some care.
//
// What about const_cast? Sadly, const_cast isn't guaranteed to work if
// the container itself was declared const. For example, the compiler is
// allowed to put a const container in a special read-only memory area
// (if such a thing exists on your platform), which would cause
// const_cast to fail at run-time. Proper uses of const_cast are few and
// far between, and this isn't one of them.
};
// So to summarize...
int main( void ) {
MyContainer c;
// cc is just a reference to the container c, that treats it as const.
const MyContainer& cc( c );
// We can access container contents without restriction going through
// lookup(), even though it is a const method...
MyContainer::MyObject& a = c.lookup( 1 );
MyContainer::MyObject& b = cc.lookup( 2 );
// But using strictLookup(), we can't get a non-const object reference
// out of a const container.
MyContainer::MyObject& x = c.strictLookup( 3 );
// illegal!
//MyContainer::MyObject& y = cc.strictLookup( 4 );
const MyContainer::MyObject& y = cc.strictLookup( 4 );
return 0;
}
// Code with prose snippets, instead of prose with code snippets. Like it?
// Hate it? Wish the whole thing was in assembly?
Read more about:
BlogsYou May Also Like