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.
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.
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.Pretty much anything that breaks the contract is readily apparent when reading/searching the code, and indicates that something needs to be rewritten.
Just because you can break the contract doesn't mean you should break the contract. Const correctness produces more readable/easily understood code.
Don't be lazy, and make life better for anyone that has to maintain and use the code after you.
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.)
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.