Our Properties: Gamasutra GameCareerGuide IndieGames Indie Royale GDC IGF Game Developer Magazine GAO
My Message close
Latest News
spacer View All spacer
 
February 10, 2012
 
Road to the IGF: Lucky Frame's Pugs Luv Beats
 
Analyst questions validity of unusual January NPD results [3]
 
DICE 2012: Blizzard's Pearce on World Of Warcraft's launch hangover
spacer
Latest Features
spacer View All spacer
 
February 10, 2012
 
arrow Principles of an Indie Game Bottom Feeder [19]
 
arrow Postmortem: CyberConnect 2's Solatorobo: Red the Hunter [1]
 
arrow Jerked Around by the Magic Circle - Clearing the Air Ten Years Later [39]
spacer
Latest Jobs
spacer View All     Post a Job     RSS spacer
 
February 10, 2012
 
Sony Computer Entertainment America LLC
Audio Tools Engineer
 
Sony Computer Entertainment America LLC
World Wide Studios Technical Product Manager
 
Sony Computer Entertainment America LLC
Senior Software Application Engineer
 
Sony Computer Entertainment America LLC
Senior Gamer Insights Specialist
 
Telltale Games
Core Technology - Senior Systems Engineer
 
High 5 Games
Technical Artist
spacer
Blogs

  Using Mutable To Write Better Container Code
by Neil Gower on 10/07/09 03:10:00 pm   Expert Blogs
11 comments Share on Twitter Share on Facebook RSS
 
 
  Posted 10/07/09 03:10:00 pm
 
// 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?
 
 
Comments

Neil Gower
profile image
strictLookup() does not allow the user to violate const-correctness. How would you implement this functionality without using mutable or redundant cut-and-paste code?

The big picture issue is the difference between bitwise const and logical const. A function like lookup() can be logically const (it doesn't change the state of the container) even though it isn't bitwise const. Mutable is the bridge between these two concepts.

sukru tikves
profile image
I think the main issue with your example is, it's not illustrative enough. You don't demonstrate a real life example, thus people will have hard time understanding what you're trying to do. And also returning references to mutable fields might not be a good idea (I'm not telling it's wrong, but using mutable means it's probably internal details, and it should be kept that way).

mutable is usually used with caching, pre-calculation, or holding external system handles transparently. Maybe you can modify your example to be less abstract.

Tyler Millican
profile image
Ah, I just got it: you're presenting the *container* as const, but the elements it points to are free game...
...right? Since they're not pointers in the example (although then mutable wouldn't be needed, really) and no other public interface is given, it seems like the elements would fall within the object's logical const. It's probably just the result of the contrived example, but I think that's where the confusion is coming from.

Mutable is one of those things where "tutorial" examples are a little harder to create since, without a clear answer to "What is the logical object?", it's hard to say "Since this isn't a part of the logical object...". Although maybe not what you were aiming at, I've found good examples of mutable in general to be some behind-the-scenes optimization (caching the results of long calculations is always a good one) or things like reference counting, gathering debugging information about the object's use, etc.


(On a side note, because of things like const_cast, unless the code is properly inlined or some global optimization is performed that allows it to verify such is the case, I doubt you'll find a compiler that's actually willing to assume that const means bitwise const for optimization purposes, static variables possibly excepted. For most purposes, const is a purely logical idea, more for the coder's sake than the code's.)

Jonathon Walsh
profile image
I really like the formatting of this article, and the content too for that matter. Seeing the whole code rather than dispersed snippets makes for an informative and concise article. Good job.

Neil Gower
profile image
Thanks for the feedback!

The real world example that inspired this is a couple thousand lines of code, so not really suitable for a blog post. Also, I wanted to make sure that this tutorialized version was complete and runnable. You can paste it into your C++ environment of choice and play around with the various ways of solving the const-but-not-really problem of implementing container element access.

If you google "C++ mutable", you'll find plenty of those caching and optimization examples Tyler and Sukru mentioned. This is a different use of mutable, but an important one if you're serious about writing const-correct code. Functions like strictLookup() come up all the time implementing iterators and containers with full const support. That could have been made clearer with an introduction - maybe 100% code is not the way to go next time. :-)

John Hahn
profile image
This is interesting because it seems like it's allowing the caller to determine the const-ness of the class members. If the caller declares the container as const and then calls strictLookup, the method will return a const object. If the caller declares the container as non-const and then calls the strictLookup, the method will return a non-const object.

This is basically the exact functionality that you get with the containers in the standard library, right? I'm assuming it's meant to create a container where the user gets to decide whether the data it contains is const or non-const.

Edit: I'm guessing the reason why this works the way it does is because if you declare an object (or reference to an object) as const, then you can't call any non-const methods for that object. Nice to know.

Neil Gower
profile image
@john: You're right. The twist here is that we don't want to copy and paste the body of strictLookup() just to change the return type from const to non-const. Mutable makes it possible to write a function like lookup() to solve this.

Blake Edwards
profile image
Why not use const_cast for this? I seem to recall a similar technique using const_cast in one of the items of Effective/More Effective C++ but can't find it now. That way you reduce duplication in the two functions differing only in constness while not allowing all other const functions to modify the member data.

Nevermind, read the last paragraph :D

Kwasi Mensah
profile image
actually, const_cast will work correctly as long as the object wasn't originally const. http://stackoverflow.com/questions/357600/is-constcast-safe. So you can implement the const version in terms of the non-const version, but you'll have to const cast the this pointer. For example:

MyObject& strictLookup( int idx ) { return contents[idx]); }
//yes, I should be using angle brackets for const_cast but the formatting treats them wonkily
const MyObject& strictLookup( int idx ) const { return (const_cast)(this)->strictLookup( idx ); }

You have to const_cast the this pointer to make sure you're calling the right version of strictLookup ( infinite recursion for the win!). Effective/More Effective C++ talks about this pattern and I'll double check when I get into work tomorrow.

Kwasi Mensah
profile image
I just re-read your comment on const_cast. The const_cast in my above comment works (won't cause any weirdness at runtime) as long as you are only reading data ( which is all the non-const version of strictLookup does). But if you're trying to share code between both versions that invariant should hold.

But using mutable will guard against undefined behavior if strictLookup does any writes (even though it shouldn't).

Kwasi Mensah
profile image
ha, you actually want to do it the other way around:


const MyObject& strictLookup const( int idx ) { return contents[idx]); }
//the " are angle brackets
MyObject& strictLookup( int idx ) { return const_cast"MObject&"( static_cast"const MyContainer&"(*this).strictLookup( idx) ); }

Bascially, you have the non-const version call the const version to avoid undefined behavior. You static cast to a const MyContainer to force the const version of the function and you const_cast the result. It's ok to const_cast in this case because if we're using the non-const version of the function we had a non-const object to begin with ( and const_cast works correctly in this case).

Effective C++ Third Edition Chapter 1 Item 3 p.24


none
 
Comment:
 




 
UBM Techweb
Game Network
Game Developers Conference | GDC Europe | GDC Online | GDC China | Gamasutra | Game Developer Magazine | Game Advertising Online
Game Career Guide | Independent Games Festival | Indie Royale | IndieGames

Other UBM TechWeb Networks
Business Technology | Business Technology Events | Telecommunications & Communications Providers

Privacy Policy | Terms of Service | Contact Us | Copyright © UBM TechWeb, All Rights Reserved.