Indexing with an arbitrary type

Jonathan M Davis via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Fri Aug 5 09:37:14 PDT 2016


On Thursday, August 04, 2016 08:13:59 Alex via Digitalmars-d-learn wrote:
> What I think about is something like this:
> https://dpaste.dzfl.pl/d37cfb8e513d

Okay, you have

enum bool isKey(T) = is(typeof(T.init < T.init) : bool);
template isNullable(T) { enum isNullable = __traits(compiles, T.init.isNull); 
}

struct IndexAble
{
    int[] arr;
    this(size_t size)
    {
        arr.length = size;
    }

    ref int opIndex(T)(T ind) if(isKey!T)
    {
        static if(isNullable!T)
            if(ind.isNull) return arr[$]; // or another manually defined state.

        return arr[ind];
    }
}

What happens if I do this?

void main()
{
    IndexAble indexable;
    indexable["hello"];
}

You get a compilation error like

q.d(17): Error: cannot implicitly convert expression (ind) of type string to 
ulong
q.d(24): Error: template instance q.IndexAble.opIndex!string error 
instantiating

because the index type of int[] is size_t, so it's only going to accept
values that are implicitly convertible to size_t, and string is not
implicitly convertible to size_t. opIndex's template constraint did not
catch the fact that passing "hello" to opIndex would result in a compilation
error. string is perfectly comparable, and it's not a valid index type for
int[] - and comparability is all that opIndex's template constraint checks
for.

For opIndex to actually catch that an argument is not going to compile if
opIndex is instantiated with that type, then its template constraint needs
to either test that the index type will implicitly convert to size_t so that
it can be used to index the int[], or it needs to test that indexing int[]
with the argument will compile. So, something like

ref int opIndex(T)(T ind)
    if(is(T : size_t))
{...}

or

ref int opIndex(T)(T ind)
    if(__traits(compiles, arr[ind]))
{...}

And given that you're dealing explicitly with int[] and not an arbitrary
type, it really makes more sense to use is(T : size_t). If IndexAble were
templated on the type for arr, then you would need to do the second. But
testing for comparability is completely insufficient for testing whether the
type can be used as an index. In fact, it's pretty much irrelevant. What
matters is whether the index type will compile with the code that it's being
used in inside the template. If you're forwarding the index variable to
another object's opIndex, then what matters is whether that opIndex will
compile with the index, and if you're doing something else with it, then
what matters is that it compiles with whatever it is that you're doing with
it.

I could declare a hash map implementation which did not use opCmp but just
used opEquals and toHash. Then I could have a struct like

struct S
{
    int i;
    int j;

    size_t toHash()
    {
        return i * j % size_t.max;
    }
}

It has an opEquals (the implicit one declared by the compiler). It has a
toHash. It does not have opCmp (and the compiler won't generate one). So,
it's not comparable. But it would work in my hash map type only used toHash
and opEquals and not opCmp.

HashMap!(S, string) hm;
hm[S(5, 22)] = "hello world";

And actually, while D's built-in AA's used to use opCmp (and thus required
it), they don't anymore. So, that code even works with the built-in AA's.
e.g.

string[S] aa;
aa[S(5, 22)] = "hello world";

So, comparability has nothing to do with whether a particular type will work
as the key for an indexable type. What matters is that the index operator be
given an argument that will compile with it - which usually means a type
which is implicitly convertible to the type that it uses for its indices but
in more complex implementations where opIndex might accept a variety of
types for whatever reason, then what matters is whether the type that you
want to pass it will compile with it. And unless all that that particular
opIndex implentation needs to care about for a type to work with it is that
the type is comparable, then testing that the type that you're going to give
it is comparable is insufficient and may not even be relevant.

- Jonathan M Davis



More information about the Digitalmars-d-learn mailing list