Kinds of containers

Jonathan M Davis via Digitalmars-d digitalmars-d at puremagic.com
Thu Oct 22 08:53:19 PDT 2015


On Thursday, 22 October 2015 at 15:26:30 UTC, bitwise wrote:
> On Thursday, 22 October 2015 at 05:09:38 UTC, deadalnix wrote:
>> The elephant in the room: make the template parameter's type 
>> qualifier transitive with the collection's qualifier.
>
> Not sure exactly what you mean by this. Currently, Ranges and 
> Cursors are templated on the type of the container, so If the 
> container is const, you get a const Range or Cursor. I haven't 
> gone over the containers with a fine tooth comb yet, so if you 
> can point anything out, it would be helpful.

LOL. const and ranges do _not_ mix well - in part because 
popFront inherently requires that the range be mutable - but 
mostly because of templates.

If you have const(T)[], and you then you slap another const on it 
- const(const(T)[]) - the compiler knows full-well that that's 
equivalent to const(T[]). In fact, it even knows that if you 
slice a const(T[]), it's perfectly safe for the result to be 
const(T)[], because it's a different array, and by keeping the 
elements const, it won't screw with the elements of the one it 
was sliced from (since they're the same elements). But what about 
with ranges?

If you have Range!(const T) and then you do const Range!(const 
T), is that equivalent to const(Range!T)? Maybe, maybe not. The 
compiler certainly doesn't treat it as such - and it can't. 
Similarly, if you have const(Range!T) or const(Range!(const T)), 
and you slice the range, the compiler doesn't magically know that 
the slice can be Range!(const T). As far as the compiler is 
concerned, we're dealing with different types here. After all, 
what if the Range type had something like this in it

struct Range(T)
{
     static if(is(T == const))
     {
         // do something differently here
     }
}

The entire guts of Range could be completely different simply due 
to a difference in constness. Template constraints could also be 
used to overload on constness. So, unlike with arrays, there 
really is no guarantee that two instantiations of the same 
template are at all related to one another even when they seem to 
only differ by constness. Heck, to even attempt to have a range 
type which has an opSlice which returns a tail-const slice 
_requires_ that you have static ifs checking for constness, or 
you end up with a recursive template instantiation.

So, even without getting containers involved, const and ranges do 
_not_ mix well, even though const and arrays get along 
fantastically. And when you have to start considering what a 
containers opSlice is going to return an how it's going to deal 
with const, things get that much worse (e.g. is it even going to 
work to have opSlice be const). And depending on the container's 
internals, having a range only have const access to its 
underlying container could be problematic. IIRC, 
std.container.Array has had some issues with const related to its 
internals not having been designed in a way that worked well with 
D's const.

So, const throws a very interesting wrench into the mix when 
dealing with either ranges or containers, and the fact that 
const(Foo!T) is not necessarily the same as const(Foo!(const T)) 
can definitely be problematic.

- Jonathan M Davis


More information about the Digitalmars-d mailing list