Valid use of the keyword mutable?

Ah hello.

I would like opinions on the use of the keyword mutable, and whether or not how I have used it, described below, is valid.  Please consider the pseudo code below:
class CStore
{
public:
	CObject* GetObject(const std::string& strName)
	{
		SortAlphabetically();
		
		// Retrieve item "strName" using lower_bound() or equivalent
	}
	void SortAlphabetically()
	{
		// Sorts m_storage alphabetically
	}

private:
	std::vector<CObject*> m_storage;
};

Open in new window


OK.  I had code similar to the above, and was looking to tidy it up.  I had the brainwave that GetObject() should be const; after all, it does not modify the object in any way.  I then had to make SortAlphabetically() const for obvious reasons.

This lead to a problem.  GetObject() should be const; but should SortAlphabetically() really also be const?  I did some reading and found out about "logical constness" vs "bitwise constness", and realised that SortAlphabetically() could be made const as although it does change the bit-pattern of the CStore object, it would not change the way the object appears to clients (so hence remains logically const).

Feeling appeased, the final piece of the solution was to make m_storage mutable, which I did, then happily continued pounding my keyboard writing new code.

For a short while.

A little nag came into my mind; having made m_storage mutable, what was to stop somebody modifying my class and writing a new function like this:
void CStore::DoNothingSpecial() const
{
	m_storage.clear();
}

Open in new window


Eeek!  Because m_storage is mutable it can be adjusted in whatever way required, so client code could call this function assuming nothing about the CStore object will change, but in reality after the call to DoNothingSpecial() it is completely different to before the call.

Am I right to be concerned with the above, or is it assumed that the anyone modifying the class will not do (stupid) things like this?

How would you get around this issue?

TIA
LVL 19
mrwad99Asked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

evilrixSenior Software Engineer (Avast)Commented:
There's a very simple rule of thumb... if the semantics of mutability are not exposed to the externals of the class then it is fine to make something mutable. In other words, mutable should be viewed as a magic trick - an illusion. The mutable entity must never be visible to anything outside the class. As long as this is the case then the smoke and mirrors of mutable maintains its illusion.

Mutable should be used to enforce the contract that your classes interface exposes. For example, if your class needs to track how many times a function is called but that function is const then making it non-const just to perform this internal house-keeping is violating the contract of the interface for no good reason or benefit to the user of the interface.

Does that make sense?
0
jkrCommented:
>> I had the brainwave that GetObject() should be const; after all, it does not modify the
>> object in any way.  I then had to make SortAlphabetically() const for obvious reasons.

Well, then 'GetObject()' isn't really 'const' at all and should not be declared as such. Or, provide both a 'const' and no-const version, e.g.

class CStore
{
public:
	CObject* GetObject(const std::string& strName)
	{
		SortAlphabetically();
		
		// Retrieve item "strName" using lower_bound() or equivalent
	}
	CObject* GetObject(const std::string& strName) const
	{
		// do NOT sort - fail if not sorted?

                if (!m_bSorted) return NULL;
		
		// Retrieve item "strName" using lower_bound() or equivalent
	}
	void SortAlphabetically()
	{
		// Sorts m_storage alphabetically

                // ...

                m_bSorted = true;
	}

private:
	std::vector<CObject*> m_storage;
        bool m_bSorted;
};
                                  

Open in new window


I agree with evilrix, yet I'd try to avoid using 'mutable' if possible at all.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
phoffricCommented:
According to Herb Sutter, for C++11, we should "embrace mutable" in our multi-threaded C++11 world. Here's an enjoyable video to watch. I'll look at it again after I learn more about C++11. It's OK to skim over the first few minutes if that gives you trouble.
    You Don’t Know const and mutable
    Now even before watching this video, I thought that for C++98 (albeit, a non-standard *nix version), that if a class had a mutex, then making the mutex mutable was the correct approach.

   I did find Herb Sutter's use of "==" in one of the slides a little confusing, and this link is supposed to clarify this point (but, for me, it opens up more questions). I'll read it again after I learn more about C++11.
   http://codecraft.co/2013/01/02/how-sutters-wrong-about-const-in-cpp-11/

For C++98, Stroustrup gives an example of a good use of mutable. Suppose you have a class which includes a Date class private data member, and a void getDate(FormatStyle &) const public method. Since get methods are usually logically const, I added the const qualifier to the method. For each format style, you could have a corresponding cached string data member that has the correctly formatted date. The cached string may be lazily computed on the first getDate() call, so you'll need a corresponding cacheValid flag that indicates that the cached string has been computed. Subsequent calls to getDate with the same format style will just fetch the cached string. getDate is logically const, even though a cached data member is sometimes being mutated. Adding the mutable qualifier to each of the cached date strings and their corresponding cacheValid flags makes sense.

Interestingly, the getDate() method is doing a read operation on the class, and yet the cache string member and the corresponding cacheValid flag are written to on the first call for a particular format style.
0
Become a Microsoft Certified Solutions Expert

This course teaches how to install and configure Windows Server 2012 R2.  It is the first step on your path to becoming a Microsoft Certified Solutions Expert (MCSE).

phoffricCommented:
BTW, for your particular case in the OP, it is unusual to have a single data member that is mutable.
0
evilrixSenior Software Engineer (Avast)Commented:
It is? Why?
0
phoffricCommented:
>> it is unusual to have a single data member that is mutable
>> It is? Why?

The OP code shows a class having a single data member. As jkr noted, " 'GetObject()' isn't really 'const' at all and should not be declared as such". If you provide a brief example of pseudo-code for a class having a single data member (that is not a mutex) and that qualifying the single data member as mutable is usual and beneficial, then I'd like to review it.

If a class has a single data member, then a method will either read it, modify it, or possibly ignore it. If reading or ignoring it, a method can be qualified as const, and having the data member qualified as mutable is meaningless. If the method is writing to the data member, then what is the benefit of marking this method as const and then having to qualify the single data member as mutable?

Limited time - travelling soon - back next week.
0
evilrixSenior Software Engineer (Avast)Commented:
I guess my point was that it was a bit of a sweeping statement. I don't think there is any sense in generalising in that way.
0
phoffricCommented:
>> Valid use of the keyword mutable?
>> I would like opinions on the use of the keyword mutable, and whether or not how I have used it, described below, is valid.
I was making first writing a statement on the author's specific example, which then transformed into a sweeping statement, which I think is true and from the general tone of the question, may be useful; but awaiting counters.
0
phoffricCommented:
Here is a Stroustrup opinion that I just looked up ["The C++ Programming Language, Special Edition", 2000]. Hope the author finds it useful.
Declaring members mutable is most appropriate when (only) part of a representation is allowed to change. If most of an object changes while the object remains logically const, it is often better to place the changing data in a separate object and access it indirectly.
Then, using this design style, he moves the cached Date formatted strings and valid flags to their own class and now mutable is not needed.
0
evilrixSenior Software Engineer (Avast)Commented:
The point of a const object is that anything exposed to the outside world, via its public/protected interface, should be immutable. If anything changes that can be perceived by the outside world you have broken the contract that your interface presents. What happens inside the class that is never exposed is fair game for being mutable (although, one should question why it needs to be mutable to ensure there isn't a fundamental flaw in the design).

In reality, it's rare you'll need to use mutable but its use isn't inherently a bad thing. Unfortunately, mutable is one of those features of C++ that is often misunderstood by those who are less well informed and, as such, it seems to be surrounded by myth and mystery. It's really not that big of a deal either way. The most important thing is that its use does not break the contract exposed by the public interface. As for how many (or few) member variables are mutable is really neither here not there. What matters is the design is sound  and robust and the use of mutable is appropriate

One important thing to consider is that using mutable could give a false sense of security when it comes to thread safety. Generally, it's considered safe to call methods on a const object in a multi-threaded environment since the contract implies nothing is going to be modified. Of course, if the class is modifying a mutable internal member this could lead to a race condition. For that reason, it should either be well documented that this const member function (or the object as a whole) isn't thread safe or you should ensure your class protects its invariants using normal thread synchronisation methods (C++11 now comes with everything you need to do this).
0
mrwad99Author Commented:
Thanks a lot for all the comments.  I re-evaluated my design and realised that allowing the underlying storage in my CStore class to be changed within a const method is a really bad idea, especially having read all the links that talk about how mutable members are typically those that are for internal housekeeping or similar.
0
evilrixSenior Software Engineer (Avast)Commented:
So, now the main question is out of the way...

...I am wondering why you are sorting the vector each time you access it? Why not just use a std::map or insert the data into the vector such that it remains sorted?
0
mrwad99Author Commented:
In order to retrieve an item using lower_bound(), binary_search() or whatever the vector must be sorted.  In reality there is a member variable that records whether or not the vector is sorted, but I didn't show that to keep things simple.

I felt the overhead of using a map was too great, having read about its performance when compared to the binary search functions with a plain vector, so didn't use it.
0
evilrixSenior Software Engineer (Avast)Commented:
>> the vector must be sorted
No why not just insert into the sorted position?

>> I felt the overhead of using a map was too great
Depends what you mean by too great? In terms of performance of retrieving, it'll be just as fast as a binary search. In terms of inserting, it'll be faster than inserting into a vector to keep it sorted (since the vector will need to shuffle stuff to make space). In terms of memory usage, yes it will use more memory than a vector; however, unless you are planning on storing millions of tiny records it's unlikely to be an overhead you need to worry about.

Don't forget, "premature optimization is the root of all evil!" Implement it using the best data structure for the job and only change it out for something less suitable if profiling demonstrates there is a bottle neck that is not acceptable.
0
mrwad99Author Commented:
Hmm; OK, I'll have a read of that article.  And for the record, I don't sort each time I insert into the vector, I only sort() when I want to retrieve, if the vector isn't already sorted, which is set to true after any insertion.  So sorting shouldn't really happen that much.
0
jkrCommented:
As a side note, I finally realized when I had to use "mutable" last time (and it's a couple of years ago): That was within the implementations of quite abstract base interfaces I designed a lot earlier, and we were adding reference counting way later on (don't ask why). So, the counter had to work in otherwise "const" methods as well. That's also the only time I used it ;o)
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C++

From novice to tech pro — start learning today.