View source code
Display the source code in object.d from which this page was generated on github.
Report a bug
If you spot a problem with this page, click here to create a Bugzilla issue.
Improve this page
Quickly fork, edit online, and submit a pull request for this page. Requires a signed-in GitHub account. This works well for small changes. If you'd like to make larger changes you may want to consider using local clone.

Function object.opEquals

Implementation for class opEquals override. Calls the class-defined methods after a null check. Please note this is not nogc right now, even if your implementation is, because of the typeinfo name string compare. This is because of dmd's dll implementation. However, it can infer to @safe if your class' opEquals is.

bool opEquals(LHS, RHS) (
  LHS lhs,
  RHS rhs
)
if ((is(LHS : const(Object)) || is(LHS : shared(const(Object)))) && (is(RHS : const(Object)) || is(RHS : shared(const(Object)))));

Example

If aliased to the same object or both null => equal

class F { int flag; this(int flag) { this.flag = flag; } }

F f;
assert(f == f); // both null
f = new F(1);
assert(f == f); // both aliased to the same object

Example

If either is null => non-equal

class F { int flag; this(int flag) { this.flag = flag; } }
F f;
assert(!(new F(0) == f));
assert(!(f == new F(0)));

Example

If same exact type => one call to method opEquals This test passes @safe because it defines a new opEquals with @safe

class F
{
    int flag;

    this(int flag)
    {
        this.flag = flag;
    }

    bool opEquals(const F o) const @safe nothrow pure
    {
        return flag == o.flag;
    }
}

F f;
writeln(new F(0)); // new F(0)
assert(!(new F(0) == new F(1)));

Example

General case => symmetric calls to method opEquals

int fEquals, gEquals;

class Base
{
    int flag;
    this(int flag)
    {
        this.flag = flag;
    }
}

class F : Base
{
    this(int flag) { super(flag); }

    bool opEquals(const Base o) @safe
    {
        fEquals++;
        return flag == o.flag;
    }
}

class G : Base
{
    this(int flag) { super(flag); }

    bool opEquals(const Base o) @safe
    {
        gEquals++;
        return flag == o.flag;
    }
}

writeln(new F(1)); // new G(1)
writeln(fEquals); // 1
writeln(gEquals); // 1

Example

This test shows an example for a comprehensive inheritance equality chain too.

static class Base
{
    int member;

    this(int member) pure @safe nothrow @nogc
    {
        this.member = member;
    }

    override bool opEquals(Object rhs) const
    {
        return this.opEquals(cast(Base) rhs);
    }

    bool opEquals(const Base rhs) const @nogc pure nothrow @safe
    {
        if (rhs is null)
            return false;
        return this.member == rhs.member;
    }
}

// works through the direct class with attributes enabled, except for pure and nogc in the current TypeInfo implementation
bool testThroughBase() nothrow @safe
{
    Base b1 = new Base(0);
    Base b2 = new Base(0);
    writeln(b1); // b2
    Base b3 = new Base(1);
    assert(b1 != b3);
    return true;
}

static assert(testThroughBase());

// also works through the base class interface thanks to the override, but no more attributes
bool testThroughObject()
{
    Object o1 = new Base(0);
    Object o2 = new Base(0);
    writeln(o1); // o2
    Object o3 = new Base(1);
    assert(o1 != o3);
    return true;
}

static assert(testThroughObject());

// Each time you make a child, you want to override all old opEquals
// and add a new overload for the new child.
static class Child : Base
{
    int member2;

    this(int member, int member2) pure @safe nothrow @nogc
    {
        super(member);
        this.member2 = member2;
    }

    // override the whole chain so it works consistently though any base
    override bool opEquals(Object rhs) const
    {
        return this.opEquals(cast(Child) rhs);
    }
    override bool opEquals(const Base rhs) const
    {
        return this.opEquals(cast(const Child) rhs);
    }
    // and then add the new overload, if necessary, to handle new members
    bool opEquals(const Child rhs) const @nogc pure nothrow @safe
    {
        if (rhs is null)
            return false;
        // can call back to the devirtualized base test with implicit conversion
        // then compare the new member too. or we could have just compared the base
        // member directly here as well.
        return Base.opEquals(rhs) && this.member2 == rhs.member2;
    }

    // a mixin template, of course, could automate this.
}

bool testThroughChild()
{
    Child a = new Child(0, 0);
    Child b = new Child(0, 1);
    assert(a != b);

    Base ba = a;
    Base bb = b;
    assert(ba != bb);

    Object oa = a;
    Object ob = b;
    assert(oa != ob);

    return true;
}

static assert(testThroughChild());

Authors

Walter Bright, Sean Kelly

License

Boost License 1.0.