Video: You Don’t Know const and mutable

imageAt C++ and Beyond in August, I gave a 30 min talk on the changed meaning of const and mutable. The talk video is now online:

You Don’t Know [keyword] and [keyword]

const means const.

Bonus: mutable is useful and continues to mean ‘already as good as const.’

This is another way C++ has become simpler: const now means what people always thought it meant, and the four-line code example doesn’t need deep analysis at all – it just works.

But we analyze the four-line example anyway as a motivating case to see why and how it works, so we can fully appreciate this new simplification in C++11…

22 thoughts on “Video: You Don’t Know const and mutable

  1. Does 17.6.5.9/3 mean that calling const methods needs to be race free?
    You can have a normal not internally synchronized class that meets the definition (does not modify the object during const method) but that doesn’t mean it’s thread safe. Simply that const methods can only contain the 2nd type of expression “the other one
    accesses […] the same memory location”. If anyone else modifies the memory location in a non-const method or externally to the class altogether that doesn’t seem to violate 17.6.5.9/3 but you create a race and aren’t thread safe.

    As sandforddene said above unless the object is unchanging in all threads the “const does not modify” guarantee is not enough to say thread safe or race free.

  2. What the part of the standard in question says is:

    “A C ++ standard library function shall not directly or indirectly modify objects (1.10) accessible by threads other than the current thread unless the objects are accessed directly or indirectly via the function’s non-const arguments, including this.”

    This is not, in terms, concerned with the thread safety of an object in the traditional sense (namely, “correctly synchronized in accordance with §1.10”). It covers a very small subset of thread safety. It requires amongst other matters a standard library function not to modify an object which it is required to view as const (say, because it was passed by reference to const) unless the object is not visible to any other thread. Since the library function will not know whether the object is visible to other threads, it in effect prevents const objects being modified by a standard library function in a multi-threaded program. Since, equally, the standard library function will not generally know whether the object was allocated in mutable storage, there are other equally compelling reasons why the standard library function should not modify the object.

    The threading requirement is a demand applied whether or not the object concerned is of itself intrinsically thread safe (say by having mutable mutexes), which I think is a defect. Having a thread-safe object does not help you once it is const.

  3. sandforddene, Andrei WAS in the room, along with Scott Meyers, as this talk was at C++ and Beyond.

  4. In short: const != thread-safe. In long:

    @Gregory – “The new additional meanings of const and mutable mean that most of the types have to be thread-safe. And I still do not get what thread-safe means here. And why const implies immutable in C++11. The immutability is not enforced in any way in the language. How can we be sure that object passed by const reference will not be mutated?”
    @Martinho Fernandes – “@Gregory: no. Only the const operations need to be allowed to called concurrently with other const operations. If all they do is read data and really modify nothing at all, then nothing else is required of the programmer. Old types that do this need no change. And this kind of const does not imply immutability (where did that idea come from?). As can be seen in the talk, it implies two things: 1) such operations do not change observable state and 2) they can be called concurrently with other const operations.”
    @Codecraft: How Sutter’s Wrong About const in C++ 11 – “So: const means unchanging in one or many threads, which is why it also implies thread-safe; ”

    What @Gregory pointed out is that bitwise const does not imply thread safety, but transitive bitwise immutability does. And since Herb Sutter and the C++11 standard library state that const implies thread-safe, it therefore also states that const objects must either be synchronized or transitively bitwise immutable. The difference between const and immutable is that ‘const means unchanging in one or many threads’ while immutable means unchanging in all threads. I.e. being able to ‘be called concurrently with other const operations’ is valid if and only if no other thread holds a mutable reference to that object. And there is no guarantee of that.

    Worse, bitwise const doesn’t even imply logical const. Nor does bitwise immutability imply logical immutability. Consider the private implementation (‘pimple’) pattern, in which an object contains a single reference to a hidden object which contains the actual implementation. That reference (should) never change, and thus the object is bitwise const/immutable, however the referenced implementation object can be mutated freely, even inside const methods. And while pimple methods should be simple wrappers, the concept applies to all child references. For example, in a linked list, it is the reference to the nodes, not the nodes themselves, which are const inside a const member function. The ability to access mutable data through a const reference, renders bitwise const neither logically const nor thread-safe.

    I am sadden that so many leaders of the C++ programming community, including the standard committee itself and the writers of our compilers, fundamentally do not understand const and it’s limitations. It’s a shame Andrei wasn’t in the room (http://dlang.org/const3.html)

    P.S. I’ve seen ‘bugs’ due to the fact the compilers will cache the contents of const objects.

  5. Just as Herb says that you would prefer ‘mutable mutex m;’ rather than use ‘const_cast(m)’ everywhere you use ‘m’ to save typing and just as Herb says ‘mutable mutex m;’ is saying that ‘m’ is thread safe such that it can be used in ‘const’ situations (i.e. const member functions), it occurred to me while watching that it’s not really ‘m’ per se that is thread safe, but ‘class mutex’ that is thread safe. So why do we need to specify ‘mutable’ on the declaration of the variables of type ‘mutex’? Why not change the language to allow you to indicate thread-safe on the class definition: ‘class mutable mutex { … };’?

    I think this is similar to @akondratskiy and I don’t completely buy Herb’s response to him as putting ‘const’ on ‘lock/unlock’ would essentially say they are thread safe. Just as Herb said in the video that a mutex is inherently thread safe. In this sense, my proposal to specify ‘mutable’ on the class definition essentially means all member functions are ‘const’.

    I feel like there is something still not quite right about the idea that const and mutable mean the same thing and imply thread safe. Herb mentions the idea that a class might have an internal counter declared as ‘mutable’ because the counter changing doesn’t really represent a state change of the object. However, this ‘mutable’ tag does not make it thread safe. You might still need to use an interlocked increment to make it thread safe. However, if the C++11 atomic class were tagged with my proposed ‘class mutable’ (e.g. ‘template class mutable atomic’), then you could just specify your counter as type ‘atomic’ and just be happy using ‘operator++’ on it, even in const member functions.

    It seems like it could be useful to be able to tag a class that is inherently thread safe. There seems to be a C++ feature trying to get out.

  6. After 10+ years of programming, I started to realize that shared data and mutability are the two major source of pain nowadays in programming world. Erlang addressed these 15 years ago and solved it very well. I’m wondering about your opinion Herb, what do you think about the actor model and immutability? Do you see it as a viable way in the future (business/performance/technology point of view)? It would be really nice to read a summary post about this, please do it :-)

  7. @Gregory: no. Only the const operations need to be allowed to called concurrently with other const operations. If all they do is read data and really modify nothing at all, then nothing else is required of the programmer. Old types that do this need no change.

    And this kind ofconst does not imply immutability (where did that idea come from?). As can be seen in the talk, it implies two things: 1) such operations do not change observable state and 2) they can be called concurrently with other const operations. #1 is not enforced by the language because the language gives leeway to the programmer to implement whatever semantics they want. However, the language does guide you towards that by making you work a bit more in order to implement a const operation that changes observable state: the compiler will reject code that would invoke non-const operations on this or modify member objects. C++ gives you a way out of this cage with mutable, but there is only one person to blame if you decide to use it in some inane way.

  8. Hi there,

    First of all…. Happy new year ! (with delay, but still counts…)

    I just came across some “professional” software engineers short discussion about the absence of modules in C++. Honestly I don’t know much about “modules”, I had to google it but the thing that really puzzled me was that they constantly called C++ “broken”…

    so… I was wondering do you have any ideas what is exactly “broken” in C++ for some people? I don’t want to ask them as they seem not very fond of the language and if it gets technical I don’t have much to say (newbie here) !

  9. PS – I’ve updated my slides to use the “implies” sign (=>) rather than “==” to be more precise that this is in additional to the “no observable side effects” meaning of const. Thanks for the comment.

  10. The new additional meanings of const and mutable mean that most of the types have to be thread-safe. And I still do not get what thread-safe means here. And why const implies immutable in C++11. The immutability is not enforced in any way in the language. How can we be sure that object passed by const reference will not be mutated?

  11. @DeadMG: Not exactly. It is technically legal for an implementation to run std::sort in parallel if it (the compiler and library in collaboration) can prove the effect is the same, including that a pure functor is being passed and that operations on the range’s elements are independent and order-insensitive, but this is generally very difficult to prove. I don’t think that this const makes that analysis significantly easier; what is needed is the idea of a pure function in the language, or a named parallel_sort algorithm that lets the programmer express his intent and promise that he’s doing it right (e.g., not passing a functor with side effects that would create a race condition).

    All: Yes, this is in addition to the existing meaning of const — it’s basically saying that const doesn’t mean just that there are no observable side effects on the object, but that there are no race-injecting side effects either, on the object directly or on other shared state.

    @akondratskiy: Taking a lock conceptually (and actually) modifies the mutex, so mutex::lock should be non-const. However, the next higher level of code that uses the mutex as an internal implementation detail may need to take a lock on that mutex as part of its own higher-level conceptually non-modifying operation, as in the talk’s examples.

    @Balog: Thinko, you got me — I was thinking of the other Schneier book with a similar title. Maybe Schneier uses “lies” and “liars” too much. :)

    @tala: The analysis is the same whether the X copy constructor takes a const X& or a const volatile X&. (C/C++ volatile has nothing to do with concurrency by the way — see my article “volatile vs volatile” at https://herbsutter.com/2009/01/12/effective-concurrency-volatile-vs-volatile/ .)

  12. Two comments:

    1) The usual (C++98) definitions of const and mutable are what the compiler is responsible for enforcing. Herb’s definitions are what the library and coder are responsible for.

    2) I think the difference between Herb’s definitions of const and mutable is that const means “thread-safe for reading” and mutable means “thread-safe for writing”.

  13. Like the previous posters asked.

    “If const means thread-safe. Why isn’t the method std::mutex::lock defined as const (as its internally synchronized)?”

    Surely it not a good idea to slap const on any method just because its internally syncronized, if observable state changes (in this case if the lock method blocks or does not)?

  14. Will it still be thread safe for a class X with a copy constructor where its first parameter is of type const volatile X& ?

  15. Perhaps it needs to be stressed that const and mutable mean “thread-safe” in addition to their old meaning, not instead of their old meaning. Any other interpretation leads to madness and chaos.

  16. Is this an old video? Secrets and lies as a decade old book. (a very good one certainly and not a bit outdated or irrelevant. ;-) The most recent was Beyond Fear until last year’s Liars and Outliers.

  17. My thoughts are similar to the previous comment. Suppose I have a concurrent queue and that all operations are correctly synchronized using mutexes. Since const means thread-safe and all of the operations are thread-safe (because I use synchronization), doesn’t that imply I should mark all of the operations const? Even the ones that do modify the *observable* state of the queue?

    If that’s the case then it’s a radical departure from how we understand const now since there’s no way I can indicate that an operation doesn’t modify the observable state of an operation. The best I can do is say that any modifications that do occur will be thread-safe. That doesn’t seem like the original intent of the const keyword and it would meaning losing a way to express valuable semantic information.

    What’s more, if applying mutable to a class member implies that the member is thread-safe, doesn’t that introduce a dangerous level of semantic coupling between the member and the containing class? What if someone changes the definition of the member type such that it is no longer thread-safe? The program would compile just fine, but it would be broken. It shouldn’t be the client’s responsibility to tell the member it’s thread safe; it should be the member types responsibility to tell all clients that it is thread-safe.

  18. Cool talk! I am wondering though – if const now means thread-safe, can’t the lock/unlock methods of std::mutex also be made const? This would mean that unique_lock could take the mutex by reference to const, and mutable would no longer be required in the example.

    In other words, should the standard library be modified to ‘bury’ mutable as deep as possible, so that users don’t even need to use it in most cases?

  19. Does this imply that it’s now completely legal for any compiler vendor to implement, say, std::sort as always in parallel?

Comments are closed.