// 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?