Reputation: 6835
If you needed to rewrite the following C++ code in D, how would you do it?
struct A{
const S* _s;
B _b;
C _c;
mutable C _c1, _c2;
A(const B& b, const C& c, const S* s){ /*...*/ }
void compute(const R& r) const
{
//...
_c1 = ...
_c2 = ...
}
};
D doesn't have mutable
, and, based on my experience, it's rarely used in C++. But, assuming mutable
is used for the right reasons here, what are my options in D?
Upvotes: 6
Views: 234
Reputation: 54280
You have three options to deal with this:
Cast away the const
. This will shit the compiler up, but there's no guarantee that your code will work as intended. In particular, if you call that function on the same object from multiple threads then you are at the mercy of data races.
Use an external data structure to store the mutable objects:
struct A
{
static C[const(A)*] _c1s, _c2s;
void compute(ref const(R) r) const
{
_c1s[&this] = ...
_c2s[&this] = ...
}
}
I'm using &this
as the key to the external hash table, but you would probably be better off using some sort of unique ID. This is a very ugly and tricky solution. I dislike it. Also, note that the hash tables are thread-local, so the same object will have different values on different threads. This may or may not be desirable for your particular application.
Rethink how you use const
in D.
In D, const
is transitive and bitwise i.e. logical const is not supported. The purpose of this is to guarantee against concurrent shared data writes. Even though your code may be logically const correct, it will still break if two threads tried to call compute
on the same object, so D disallows it and provides no legal escape (no mutable
).
Essentially, you should mark functions as const
only when they are bitwise const.
The result of this is that you should use const
a lot less in D than you would in C++ because you require bitwise const a lot less than you require logical const.
As an example, consider a simple (pointless) generic equal
function that tells you if two objects are equal:
bool equal(T)(T lhs, T rhs) { return lhs == rhs; }
Notice that I haven't marked the function parameters as const
. This is on purpose. Testing for equality should not require bitwise const -- it only requires logical const, so enforcing D's level of const on the objects would be needlessly restrictive.
As jA_cOp says, the D community sees no room for logical const in D, for better or for worse. The problem arises when you try to use D's const like C++'s const. They are not the same, so don't use them in the same way! If there's any possibility at all that a function may require the use of logical const then do not mark them as bitwise const!
Upvotes: 3
Reputation: 78615
Short version; you can't, by design.
Longer version, the designers of D concluded (after some epic debates) that the benefits of mutable are outweighed by the downsides. (See: jA_cOp's answer for some of the details, many of the other reaons are driven by a intent to make concurrent programming and the reasoning there of less ugly.)
Upvotes: 4
Reputation: 3305
D's immutable
is transitive, when given an immutable reference (such as the this
reference in an immutable member function), all fields are immutable too. There is no way around this in D. const
only exists in D to bind mutable and immutable data, but since immutable is implicitly convertible to const, it has to be transitive too. Once you go immutable (or const), you can't go back.
The benefits are several: immutable data can be shared across threads safely, can be put in ROM if desirable, and is easy to reason with.
There simply is no room for logical const in D, for better or worse.
Upvotes: 6