I think that it would be appropriate to post the basic conclusions of the recent thread on this topic on the D newsgroup here, so that those who don't track that list can still get the appropriate answer.
D's const is not logical const. It is transitive and fully const. The language does not technically support logical const. The language does not define any way to mutate a const object.
And actually, C++ doesn't have logical const either. Using mutable
and casting away const-ness allows you to totally circumvent const, such that, technically-speaking, const doesn't actually guarantee anything except that the you aren't calling any non-const functions on a const variable. The fact that const functions are actually const and don't screw with your variables is completely held together by convention. Now, most programmers don't go around casting away const-ness left and right and making everything mutable, so in practice, it's quite useful, but it not only can be completely circumvented, but the lanuage specifically gives you defined means of doing so. In C++ mutable
and casting away const are well-defined and supported by the language.
D doesn't do that. D's const is actually const. Casting away const on a variable and then altering it is undefined. There is no mutable. D's const has real guarantees (as long as you don't do anything undefined like cast away const on something and then mutate it). This is important, not only because the compiler guarantees for D are much stronger than those for C++, but because immutable variables can't be altered in any way shape or form. They could be in read-only memory, and who knows what horrid things would happen if you tried to cast away immutability and alter such a variable (a segfault would likely be the nicest thing that could happen). And since a const variable could actually refer to immutable data, casting away const to alter a variable or allowing for const variables to somehow be altered would be bad, to say the least. So, the language doesn't allow it.
Now, as BCS points out, D is a pragmatic language. You can cast away const, at which point you could alter the variable. So, for instance, you could have a variable which was used to cache the return value of a const function (presumably with that cache being invalidated if the state of the object changed) and cast away const to change it. As long as the variable in question is not actually immutable, it will work. However, this is undefined behavior. Once you do it, you're on your own. You're bypassing the type system and the compiler's guarantees. You are the one responsible for making sure that you don't do it on an immutable object or otherwise screw up what the compiler normally gurantees. So, if you need to do it, you can, but you're stepping out into the Wild West, and it's up to you to make sure that you aren't mutating what you shouldn't.
Given that casting away const will work as long as the variable doesn't actually refer to immutable data, it is possible to create a Mutable
template to essentially get what mutable
gives you in C++ (so, it'll do the casting away of const-ness for you). he_the_great gives an example of such a template in his answer. But using such a template is still undefined behavior. Using it on an object which is actually immutable is going to cause problems. You, the programmer, must make sure that it's used correctly.
So, D makes it technically possible to have logical const by casting away const, but in order to do it, you have to step outside of what the compiler guarantees by bypassing the type system, and you must make sure that you don't misuse it and mutate variables which shouldn't/can't be mutated, or your code will have problems - segfaults quite possibly being the least among them.
EDIT: I forgot to mention the one proposed solution which does not break the type system. As long as you're willing to forgoe purity, you can use a global variable of some variety (be it at module scope, a class variable, or a struct variable) to hold your cached values. The const function can freely use and mutate the global variables, so it can be used in lieu of the missing mutable
. That does mean, however, that the function can't be pure, which could also be a big problem. It is, however, a way to have a const function still be able to mutate the data that it needs to without breaking the type system.