casting away const and then mutating

Steven Schveighoffer via Digitalmars-d digitalmars-d at puremagic.com
Fri Jul 24 13:44:44 PDT 2015


On 7/24/15 4:20 PM, Timon Gehr wrote:
> On 07/24/2015 10:08 PM, Steven Schveighoffer wrote:
>>
>> We can start with "casting away const and mutating, even if you know the
>> underlying data is mutable, is UB, except for these situations:..."
>>
>> And relax from there.
>
> But what is the point?

The original PR is to add a const and immutable version of upperBound 
and lowerBound to RedBlackTree.

Both of these functions are effectively const. But we must return a 
range that matches the constancy of the tree itself.

So for example:

RedBlackTree!int m;
const RedBlackTree!int c = m;

auto x = m.upperBound(5); // should return range over mutable ints
auto y = c.upperBound(5); // should return range over const ints.

The chosen implementation was to cast away const inside the const 
upperBound function, and run the mutable one, knowing that the actual 
algorithm doesn't modify any data. But I objected saying that it's 
better to run the code as const, and cast away const at the end in the 
mutable version, since the compiler will then be mechanically ensuring 
the const promise in the case of a const RedBlackTree.

The resulting discussion was that this is undefined behavior. But 
upperBound itself isn't modifying any data, it's just restoring the 
constancy of the range. But the range itself could potentially be used 
to modify the data. It didn't seem to me like this should be undefined 
behavior, since the compiler would have to make a very long connection 
through the various calls in order to see that everything would be const.

inout would work perfectly here, except you can't create a custom struct 
with an inout member that implicitly casts back to mutable/const/immutable.

So I don't know the answer. It seems very bad to cast away const to run 
a complex algorithm without mechanical checking. But ironically, that 
may be the only defined way to do it (aside from copy-paste 
implementation, or using a templated implementation).

The advantage of simply clarifying the spec is that the current compiler 
behavior (which should work) doesn't need to change, we just change the 
spec.

Ideally, we should just fix the situation with tail-const and we could 
have the best answer.

I think I'll give up on this argument. There isn't much use in putting 
in a rule for the spec that covers over a missing feature that we will 
likely add later.

Also, I just thought of a better way to do this that doesn't require any 
casting.

Forget this thread ever happened :)

-Steve


More information about the Digitalmars-d mailing list