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.
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.