D言語でclass同士のopEqualsをオーバーロードする

D言語のclass同士の等価比較演算子オーバーロードは少々特殊です。

structだと

struct S {
    int x;
    bool opEquals(S s) {
        return x == s.x;
    }
}

と書けますがclassの場合だと

class C {
    int x;
    override bool opEquals(Object o) {
        auto c = cast(C)o;
        if (c is null) return false;
        return x == c.x;
    }
}

のように書きます。 D言語ではclassにopEqualsが実装されてる際にはobject.opEqualsの定義に従って比較がされます。 object.opEqualsは以下のように定義されています(実際のソースコードは多少の違いがありますが)

bool opEquals(Object a, Object b)
{
    if (a is b) return true;
    if (a is null || b is null) return false;
    if (typeid(a) == typeid(b)) return a.opEquals(b);
    return a.opEquals(b) && b.opEquals(a);
}

この定義を見れば分かるようにopEqualsには比較対象がなんであってもObjectが渡されるので引数はObjectでなくてはいけません。また、全てのクラスの基底クラスであるObjectには

class Object{
//省略
    bool opEquals(Object o) {
        return this is o;
    }
//省略
}

とopEqualsが実装されているのでopEqualsにはoverride修飾子を付けて定義しなければいけません。

D言語ではObjectから任意のクラスへのキャストが不正なものであった場合は結果がnullになるのでまずnullになっていない(不正な変換ではない)かをチェックします。 そして比較のコードを書くようになります。

immutable class

immutable class IC {
    int x;
    this (int x) {
         this.x = x;
    }
    override bool opEquals(Object o) {
        auto ic = cast(IC)o;
        if (ic is null) return false;
        return x == ic.x;
    }
}

このように定義した場合

Error: function dmatch.core.parser.AST.opEquals does not override any function, did you mean to override 'object.Object.opEquals'?

というエラーメッセージが出ます。原因はimmutable classとした場合immutableが付いているので実際にはbool opEquals (Object o){}でなくなってしまうためobject.Object.opEqualsをオーバーライドすることが出来ないためです。そのため、この場合はclassの定義自体を

class IC {
    immutable int x;
    this (int x) immutable {
         this.x = x;
    }
    override bool opEquals(Object o) {
        auto ic = cast(IC)o;
        if (ic is null) return false;
        return x == ic.x;
    }
}

と変更してやると動くようになります。 僕はこれでハマったのですが lempiji@思秋期 (@lempiji) | Twitterさんにこの解決法を教えていただき解決することが出来ました。ありがとうございました。