Behavior of opEquals

w0rp via Digitalmars-d digitalmars-d at puremagic.com
Wed Sep 2 13:25:14 PDT 2015


On Wednesday, 2 September 2015 at 18:57:11 UTC, Jacob Carlborg 
wrote:
> I encountered a problem in the implementation of 
> std.xml.Document.opEquals (yes, I've reported an issue). The 
> problem is demonstrated with this example:
>
> class Base
> {
>     int a;
>
>     override bool opEquals (Object o)
>     {
>         if (auto base = cast(Base) o)
>             return base.a == a;
>         else
>             return false;
>     }
> }
>
> class Foo : Base
> {
>     int b;
>
>     override bool opEquals (Object o)
>     {
>         if (auto foo = cast(Foo) o)
>             return super == cast(Base) foo && foo.b == b;
>         else
>             return false;
>     }
> }
>
> void main()
> {
>     auto f1 = new Foo;
>     auto f2 = new Foo;
>     assert(f1 == f2);
> }
>
> This code will result in an infinite recursion. I think the 
> problem is in the super call, due to == being rewritten to call 
> object.opEquals. The implementation of object.opEquals will 
> call opEquals on the actual instances. The call will be 
> dynamically resolved and end up calling Foo.opEquals instead of 
> Base.opEquals.
>
> Is this really good behavior, something a developer would 
> expect? I mean, in every other case calling super.someMethod 
> will actually call the method in the base class.
>
> In this case the solution/workaround is to explicitly call 
> super.opEquals, but that will miss some optimizations 
> implemented in object.opEquals.

Yeah, I would just call super.opEquals, like so.

class Base {
     int a;

     override bool opEquals(Object o) {
         if (auto other = cast(Base) o)
             return a == other.a;

         return false;
     }
}

class Foo : Base {
     int b;

     override bool opEquals(Object o) {
         if (!super.opEquals(o))
             return false;

         if (auto other = cast(Foo) o)
             return b == other.b;

         return false;
     }
}

void main()
{
     auto f1 = new Foo;
     auto f2 = new Foo;
     assert(f1 == f2);
}

If some optimisations are missed by structuring the methods in 
this way, then maybe that's something the compiler should be 
programmed to handle.


More information about the Digitalmars-d mailing list