Disclaimer: I am aware that there are two questions about the usefulness of const-correctness, however, none discussed how const-correctness is necessary in C++ as oppos
This talk and video from Herb Sutter explains the new connotations of const
with regards to thread-safety.
Constness might not have been something you had to worry about too much before but with C++11 if you want to write thread-safe code you need to understand the significance of const
and mutable
Actually, it's not... not entirely, anyway.
In other languages, especially functional or hybrid languages, like Haskell, D, Rust, and Scala, you have the concept of mutability: variables can be mutable, or immutable, and are usually immutable by default.
This lets you (and your compiler/interpreter) reason better about functions: if you know that a function only takes immutable arguments, then you know that function isn't the one that's mutating your variable and causing a bug.
C and C++ do something similar using const, except that it's a much less firm guarantee: the immutability isn't enforced; a function further down the call stack could cast away the constness, and mutate your data, but that would be a deliberate violation of the API contract. So the intention or best practice is for it to work quite like immutability in other languages.
All that said, C++ 11 now has an actual mutable keyword, alongside the more limited const keyword.
Const correctness provides two notable advantages to C++ that I can think of, one of which makes it rather unique.
const_cast
you have compiler-checked safety with regards to mutable vs. immutable data.const
, the compiler is free to place it in read-only memory. This can particularly matter in embedded systems. C++ supports this; few other languages do. This also means that, in the general case, you cannot safely cast const
away, although in practice you can do so in most environments.C++ isn't the only language with const correctness or something like it. OCaml and Standard ML have a similar concept with different terminology — almost all data is immutable (const), and when you want something to be mutable you use a different type (a ref
type) to accomplish that. So it's just unique to C++ within its neighboring languages.
Finally, coming the other direction: there have been times I have wanted const in Java. final
sometimes doesn't go far enough as far as creating plainly immutable data (especially immutable views of mutable data), and don't want to create interfaces. Look at the Unmodifiable collection support in the Java API and the fact that it only checks at run time whether modification is allowed for an example of why const is useful (or at least the interface structure should be deepend to have List and MutableList) — there is no reason that attempting to mutate an immutable structure can't be a compile-type error.
You want to use const in methods as well in order to take advantage of return value optimization. See Scott Meyers More Effective C++ item 20.
Programming is writing in a language that will be ultimately processed by the computer, but that is both a way of communicating with the computer and other programmers in the same project. When you use a language, you are restricted to the concepts that can be expressed in it, and const is just one more concept you can use to describe your problem, and your solution.
Constantness enables you to express clearly from the design board to the code one concept that other languages lack. As you come from a language that does not have it, you may seem puzzled by a concept you have never used --if you never used it before, how important can it be?
Language and thought are tightly coupled. You can only express your thoughts in the language you speak, but the language also changes the way you think. The fact that you did not have the const keyword in the languages you worked with implies that you have already found other solutions to the same problems, and those solutions are what seems natural to you.
In the question you argued that you can provide a non mutating interface that can be used by functions that do not need to change the contents of the objects. If you think about it for a second, this same sentence is telling you why const is a concept you want to work with. Having to define a non-mutating interface and implement it in your class is a work around the fact that you cannot express that concept in your language.
Constantness allows you to express those concepts in a language that the compiler (and other programers) can understand. You are establishing a compromise on what you will do with the parameters you receive, the references you store, or defining limits on what the users of your class are allowed to do with the references you provide. Pretty much each non-trivial class can have a state represented by attributes, and in many cases there are invariants that must be kept. The language lets you define functions that offer access to some internal data while at the same time limits the access to a read-only view that guarantees no external code will break your invariants.
This is the concept I miss more when moving to other languages. Consider an scenario where you have a class C that has among others an attribute a of type A that must be visible to external code (users of your class must be able to query for some information on a). If the type of A has any mutating operation, then to keep user code from changing your internal state, you must create a copy of a and return it. The programmer of the class must be aware that a copy must be performed and must perform the (possibly expensive) copy. On the other hand, if you could express constantness in the language, you could just return a constant reference to the object (actually a reference to a constant view of the object) and just return the internal element. This will allow the user code to call any method of the object that is checked as non-mutating, thus preserving your class invariants.
The problem/advantage, all depends on the point of view, of constantness is that it is viral. When you offer a constant reference to an object, only those methods flagged as non-mutating can be called, and you must tell the compiler which of the methods have this property. When you declare a method constant, you are telling the compiler that user code that calls that method will keep the object state. When you define (implement) a method that has a constant signature, the compiler will remind you of your promise and actually require that you do not internally modify the data.
The language enables you to tell the compiler properties of your methods that you cannot express any other way, and at the same time, the compiler will tell you when you are not complying with your design and try to modify the data.
In this context, const_cast<> should never be used, as the results can take you into the realm of undefined behavior (both from a language point of view: the object could be in read-only memory, and from a program point of view: you might be breaking invariants in other classes). But that, of course, you already know if you read the C++FAQ lite.
As a side note, the final keyword in Java has really nothing to do with the const keyword in C++ when you are dealing with references (in C++ references or pointers). The final keyword modifies the local variable to which it refers, whether a basic type or a reference, but is not a modifier of the referred object. That is, you can call mutating methods through a final reference and thus provide changes in the state of the object referred. In C++, references are always constant (you can only bind them to an object/variable during construction) and the const keyword modifies how the user code can deal with the referred object. (In case of pointers, you can use the const keyword both for the datum and the pointer: X const * const declares a constant pointer to a constant X)
For example you have a funcion:
void const_print(const char* str)
{
cout << str << endl;
}
Another method
void print(char* str)
{
cout << str << endl;
}
In main:
int main(int argc, char **argv)
{
const_print("Hello");
print("Hello"); // syntax error
}
This because "hello" is a const char pointer, the (C-style) string is put in read only memory. But it's useful overall when the programmer knows that the value will not be changed.So to get a compiler error instead of a segmentation fault. Like in non-wanted assignments:
const int a;
int b;
if(a=b) {;} //for mistake
Since the left operand is a const int.