In my opinion you should make your mind about what the class does: is it mutable? then don't use it with const. Unfortunately, you are still required to use const in many places: copy constructors and operators, for example. However, you can still choose to minimize the use of const, and avoid it whenever possible.
I would say that constness is a massive boon so I use it on the vast majority of declarations. It reduces cognitive load by being able to assume that variables are not going to change.
include <iostream>
void foo(const int& a, int& b)
{
std::cout << "a: " << a << std::endl;
std::cout << "b: " << b << std::endl;
++b;
std::cout << "a: " << a << std::endl;
std::cout << "b: " << b << std::endl;
}
int main() {
int a = 1;
foo(a, a);
}
Here the value of a changes in the middle of the function even though it's a const reference, because of aliasing. Here const doesn't mean the value won't change--only that you can't change it through that particular reference. Yes, const is useful, but if you don't know what it actually does it will bite you in the ass, just like most of C++.A const reference guarantees to the caller that the callee won't modify the object, not to the callee that the object won't be modified.
It's to be expect that we can change a-through-&b, but not a-through-&a in foo.
You either need to be very strict about creating non-const references to objects, or you need to enforce immutability at the class level. Unfortunately the latter has all sorts of other practical problems, often including efficiency trade-offs or awkward APIs.
And this is exactly why const means pretty close to nothing. Compilers can't enforce it in so many situations that it is almost like the "auto" keyword in pre-C++11 times.
If you want the compiler to actually enforce safety properties of the language, use a language like Haskell or Rust.
In addition, to enforce immutability in C++, you have to disable copy/move constructors and assignment operators, which removes much benefit of using values instead of references.
But const cannot guarantee this, because it can be cast away so easily. This could be true just for your own code, but then it is you who is responsible for maintaining the immutability, not the compiler.
By this logic, none of the static type checks in C++ is of any use, since they can be cast away easily as well.