No. However, most of the time they will be.
While it's helpful to think of
const as "thread-safe" and
mutable as "(already) thread-safe",
const is still fundamentally tied to the notion of promising "I won't change this value". It always will be.
I have a long-ish train of thought so bear with me.
In my own programming, I put
const everywhere. If I have a value, it's a bad thing to change it unless I say I want to. If you try to purposefully modify a const-object, you get a compile-time error (easy to fix and no shippable result!). If you accidentally modify a non-const object, you get a runtime programming error, a bug in a compiled application, and a headache. So it's better to err on the former side and keep things
bool is_even(const unsigned x)
return (x % 2) == 0;
bool is_prime(const unsigned x)
return /* left as an exercise for the reader */;
template <typename Iterator>
void print_special_numbers(const Iterator first, const Iterator last)
for (auto iter = first; iter != last; ++iter)
const auto& x = *iter;
const bool isEven = is_even(x);
const bool isPrime = is_prime(x);
if (isEven && isPrime)
std::cout << "Special number! " << x << std::endl;
Why are the parameter types for
const? Because from an implementation point of view, changing the number I'm testing would be an error! Why
const auto& x? Because I don't intend on changing that value, and I want the compiler to yell at me if I do. Same with
isPrime: the result of this test should not change, so enforce it.
const member functions are merely a way to give
this a type of the form
const T*. It says "it would be an error in implementation if I were to change some of my members".
mutable says "except me". This is where the "old" notion of "logically const" comes from. Consider the common use-case he gave: a mutex member. You need to lock this mutex to ensure your program is correct, so you need to modify it. You don't want the function to be non-const, though, because it would be an error to modify any other member. So you make it
const and mark the mutex as
None of this has to do with thread-safety.
I think it's one step too far to say the new definitions replace the old ideas given above; they merely compliment it from another view, that of thread-safety.
Now the point of view Herb gives that if you have
const functions, they need to be thread-safe to be safely usable by the standard library. As a corollary of this, the only members you should really mark as
mutable are those that are already thread-safe, because they are modifiable from a
void act() const
mNotThreadSafe = "oh crap! const meant I would be thread-safe!";
mutable std::string mNotThreadSafe;
Okay, so we know that thread-safe things can be marked as
mutable, you ask: should they be?
I think we have to consider both view simultaneously. From Herb's new point of view, yes. They are thread safe so do not need to be bound by the const-ness of the function. But just because they can safely be excused from the constraints of
const doesn't mean they have to be. I still need to consider: would it be an error in implementation if I did modify that member? If so, it needs to not be
There's a granularity issue here: some functions may need to modify the would-be
mutable member while others don't. This is like wanting only some functions to have friend-like access, but we can only friend the entire class. (It's a language design issue.)
In this case, you should err on the side of
Herb spoke just slightly too loosely when he gave a
const_cast example an declared it safe. Consider:
void act() const
This is safe under most circumstances, except when the
foo object itself is
x.act(); // okay
const foo y;
y.act(); // UB!
This is covered elsewhere on SO, but
const foo, implies the
counter member is also
const, and modifying a
const object is undefined behavior.
This is why you should err on the side of
const_cast does not quite give you the same guarantees. Had
counter been marked
mutable, it wouldn't have been a
Okay, so if we need it
mutable in one spot we need it everywhere, and we just need to be careful in the cases where we don't. Surely this means all thread-safe members should be marked
Well no, because not all thread-safe members are there for internal synchronization. The most trivial example is some sort of wrapper class (not always best practice but they exist):
const_container_view other_missing_function_I_really_want() const
Here we are wrapping
threadsafe_container and providing another member function we want (would be better as a free function in practice). No need for
mutable here, the correctness from the old point of view utterly trumps: in one function I'm modifying the container and that's okay because I didn't say I wouldn't (omitting
const), and in the other I'm not modifying the container and ensure I'm keeping that promise (omitting
I think Herb is arguing the most cases where we'd use
mutable we're also using some sort of internal (thread-safe) synchronization object, and I agree. Ergo his point of view works most of the time. But there exist cases where I simply happen to have a thread-safe object and merely treat it as yet another member; in this case we fall back on the old and fundamental use of