Not sure if this is a style question, or something that has a hard rule...
If I want to keep the public method interface as const as possible, but make the object thread safe, should I use mutable mutexes? In general, is this good style, or should a non-const method interface be preferred? Please justify your view.
[Answer edited]
Basically using const methods with mutable mutexes is a good idea (don't return references by the way, make sure to return by value), at least to indicate they do not modify the object. Mutexes should not be const, it would be a shameless lie to define lock/unlock methods as const...
Actually this (and memoization) are the only fair uses I see of the
mutable
keyword.You could also use a mutex which is external to your object: arrange for all your methods to be reentrant, and have the user manage the lock herself :
{ lock locker(the_mutex); obj.foo(); }
is not that hard to type, andhas the advantage it doesn't require two mutex locks (and you are guaranteed the state of the object did not change).
The hidden question is: where do you put the mutex protecting your class?
As a summary, let's say you want to read the content of an object which is protected by a mutex.
The "read" method should be semantically "const" because it does not change the object itself. But to read the value, you need to lock a mutex, extract the value, and then unlock the mutex, meaning the mutex itself must be modified, meaning the mutex itself can't be "const".
If the mutex is external
Then everything's ok. The object can be "const", and the mutex don't need to be:
IMHO, this is a bad solution, because anyone could reuse the mutex to protect something else. Including you. In fact, you will betray yourself because, if your code is complex enough, you'll just be confused about what this or that mutex is exactly protecting.
I know: I was victim of that problem.
If the mutex is internal
For encapsulation purposes, you should put the mutex as near as possible from the object it's protecting.
Usually, you'll write a class with a mutex inside. But sooner or later, you'll need to protect some complex STL structure, or whatever thing written by another without mutex inside (which is a good thing).
A good way to do this is to derive the original object with an inheriting template adding the mutex feature:
This way, you can write:
The problem is that it won't work because
object
is const, and the lock object is calling the non-constlock
andunlock
methods.The Dilemma
If you believe
const
is limited to bitwise const objects, then you're screwed, and must go back to the "external mutex solution".The solution is to admit
const
is more a semantic qualifier (as isvolatile
when used as a method qualifier of classes). You are hiding the fact the class is not fullyconst
but still make sure provide an implementation that keeps the promise that the meaningful parts of the class won't be changed when calling aconst
method.You must then declare your mutex mutable, and the lock/unlock methods
const
:The internal mutex solution is a good one IMHO: Having to objects declared one near the other in one hand, and having them both aggregated in a wrapper in the other hand, is the same thing in the end.
But the aggregation has the following pros:
So, keep your mutex as near as possible to the mutexed object (e.g. using the Mutexed construct above), and go for the
mutable
qualifier for the mutex.Edit 2013-01-04
Apparently, Herb Sutter have the same viewpoint: His presentation about the "new" meanings of
const
andmutable
in C++11 is very enlightening:http://herbsutter.com/2013/01/01/video-you-dont-know-const-and-mutable/