If you used const correctly, you couldn't change anything about the class, therefore it was also thread-safe. If your class had mutable members or got around the const guarantee through backdoors (e.g. pointer passed in that modified another object, or member pointers that called non-const methods), it wasn't correctly const and shouldn't have been marked as such.
There is nothing wrong with the const keyword, just with programmers who marked things const that broke the const contract. There is no language ambiguity on this.
The language doesn't define "correct const usage" the way you see it. It only specifies what is defined and undefined behavior. Casting a pointer-to-const to a pointer-to-non-const and modifying an object via that is defined behavior as long as the original object is not declared const. (see my comment above)
And that's where functional programming languages are gaining over imperative/oo languages for multi-threaded behavior. Because you have the ability to break the contract, it produces code that is harder to follow, harder to learn for beginners, and harder to debug in parallel/concurrent contexts.
It's a shame that this is more best practice. Hopefully the specification will address this in the future that you can only call const from const, and cast from const to non-const can't be used.
Completely agree. Despite what gurus say, there is hardly an advantage in writing const correct code, other than complying with existing libraries. Const is a nice concept in theory, but it doesn't provide any guarantees, since it is so easy to cast const away from any pointer. As a result, compilers can't do anything in practice with const, other than give an endless stream of compilation errors.
I've actually just looked at the standard and there is a clear example something like this:
int x;
int const *cpx = &x;
int *px = const_cast<int *>(cpx);
*px = 1; // defined behavior, modifying non-const object through non-const pointer
While this may seem ok here, consider that the same applies to functions taking pointers to const. While this usually means the function won't modify the pointed-to objects, there is no guarantee from the language about that.
Often, the point of stuff like 'const' is exactly to give a stream of compilation errors, because they presumably mean the programmer is doing something wrong (either using the const keyword wrong, or using the things marked const incorrectly)
Even if things like 'const' are useless to the compiler in optimisation terms, they provide (weak) contracts for the humans writing the code, and obvious flags when these contracts are broken, by having to be explicit about, in this instance, const_cast's. ('git grep const_cast' => places to examine closely for bugs.)
The problem is that const is also flawed as a contract. Whenever you're using const, you're trying to design a class that behaves as a mutable or as an immutable object, depending on how the pointer/reference is declared. A class that behaves in two different ways depending on the situation is a recipe for disaster, and that's exactly what const asks us to do.
I only noticed this problem when I learned how languages such as Smalltalk/objective-c deal with the same issue: they create different classes for each situation, so if you want a mutable array you have to say so.