On Wednesday, 23 July 2014 at 16:47:40 UTC, H. S. Teoh via Digitalmars-d wrote:
This morning, I discovered this major WAT in D:

----
struct S {
        int x;
        int y;
        int opCmp(S s) {
                return x - s.x; // compare only x
        }
}

void main() {
        auto s1 = S(1,2);
        auto s2 = S(1,3);
        auto s3 = S(2,1);

        assert(s1 < s3); // OK
        assert(s2 < s3); // OK
        assert(s3 > s1); // OK
        assert(s3 > s2); // OK
        assert(s1 <= s2 && s2 >= s1); // OK
        assert(s1 == s2); // FAIL -- WAT??
}
----

The reason for this is that the <, <=, >=, > operators are defined in terms of opCmp (which, btw, is defined to return 0 when the objects being compared are equal), but == is defined in terms of opEquals. When
opEquals is not defined, it defaults to the built-in compiler
definition, which is a membership equality test, even if opCmp *is*
defined, and returns 0 when the objects are equal.

Why isn't "a==b" rewritten as "a.opCmp(b)==0"?? I'm pretty sure TDPL says this is the case (unfortunately I'm at work so I can't check my
copy of TDPL).

https://issues.dlang.org/show_bug.cgi?id=13179

:-(

I would argue that the compiler should still be generating opEquals even if opCmp is defined. Otherwise, even if opCmp is consistent with the built-in opEquals, you'll be forced to reimplement opEquals - and toHash if you're using that type as a key, since once you define opEquals, you have to define toHash.

If it makes sense for a type to define opCmp but not define opEquals (which I seriously question), then I think that it should be explicit, in which case, we can use @disable, e.g. something like

struct S
{
    @disable bool opEquals(ref S s);

    int opCmp(ref S S)
    {
        ...
    }

    ...
}

- Jonathan M Davis

Reply via email to