The video you cited is for an advanced and expert audience. Herb Sutter tries to bring this audience to a consensus on how to best communicate the intent of these keywords to other people, in this age of everything yearning to be thread-safe.
So don't expect that this video contains all you need to know to write perfectly thread-safe code.
I'm marking every private member as mutable because they're either protected by a mutex or thread-safe (by their implementation) - which doesn't feel right (I'm inexperienced and do not have a basis for this).
Here is a hypothetical example of what would be blatantly wrong:
class Size
{
mutable int x;
mutable int y;
// ...
public:
void setSize(int newX, int newY) const // (first scream)
{
// modify members "x" and "y", "SAFELY!" (second scream)
}
};
The function signature screams wrong, because a function named setSize
couldn't possibly be const
. The implementation of the function's code would eventually require marking the members "x" and "y" as mutable
, which would scream wrong too.
It doesn't matter what code is in the setSize
function. If the function's name implies that the function will change the state of the object, it should not be marked with const
. Let's suppose we remove the const
keyword from the function. Now we find that we don't have to use the mutable
keyword on the members "x" and "y". Problem solved.
In other words, if the use of const
and mutable
are both against intuition, and if such awkward usage occur in pairs, then one should to be suspicious of their usage being wrong.
However, the video mentions one exceptional case. If a class contains a std::mutex
member, it is almost always correct to mark it as a mutable
member. Please refer to the video for explanations.
Once you have some experience doing it correctly, you can rely on your intuition to see whether it looks natural, and natural means correct.
If a function's name implies that it should not change the state of the object, then it should be marked with const
, and the code inside needs to be made thread-safe, by not performing thread-unsafe operations.
This is the mentality expressed in the video linked above. When it comes to thread-safety, users of your code (yourself, or your fellow teammates) have certain expectations that certain functions shouldn't do certain things.
The video mentions these examples:
bool operator == (const T& other) const
should never modify this
or other
, because nobody would expect comparisons to modify anything. (This is called "side-effect free".)
class T { public: T(const T& other) {...} };
should never modify other
, because the same instance of other
might have been passed into two threads, each threading passing it into the copy constructor.
How about this code?
class Size
{
const int x;
const int y;
// ...
public:
Size(int init_x, int init_y)
: x(init_x), y(init_y)
{
}
};
This code is even better. It demonstrates the correct use of const
on members.
However, it does prevent one from using the Size
class in certain ways. For example, you cannot use assignment operators to overwrite an existing instance of Size
. In other words, following the best practice when implementing the Size
class, may require you to redesign other source code that uses Size
. This is the "contagious issue" that makes const-ness an important consideration for a C++ project.
const
objects work well with thread-safety because they are immutable. Even if you share references to the const object, you won't need to have any locking mechanisms to protect accessing the const object. However, mutable objects are where most problems with thread-safety occur. Some languages (like Erlang) go so far as to codify that in the language. C++const
members are initialized in the constructor and never changed, which is really what makes it threadsafe.const
. It merely says thatconst
things are thread-safe.const
, people will expect to be able to call such a function on that object from multiple threads. So if that function would do something where that's not the case, you need to deal with it within theconst
function(s). For most users, their code need not change, since it implicitly does that anyway.const
". It's merely "const
ought to be thread safe".