Link to home
Start Free TrialLog in
Avatar of mrwad99
mrwad99Flag for United Kingdom of Great Britain and Northern Ireland

asked on

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
Avatar of evilrix
evilrix
Flag of United Kingdom of Great Britain and Northern Ireland image

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?
ASKER CERTIFIED SOLUTION
Avatar of jkr
jkr
Flag of Germany image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of phoffric
phoffric

BTW, for your particular case in the OP, it is unusual to have a single data member that is mutable.
It is? Why?
>> 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.
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.
>> 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.
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.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of mrwad99

ASKER

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.
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?
Avatar of mrwad99

ASKER

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.
>> 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.
Avatar of mrwad99

ASKER

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.
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)