none
Operator == implementation for user-defined ref class RRS feed

  • Question

  • In my own ref class I implement a static operator == which takes my ref class as the first parameter type and my ref class as the second parameter type, in order to override reference equality. This works correctly as expected.

    Now if I implement another operator == which takes my ref class as the first parameter type and a different ref class as the second parameter type, this works correctly except when specifying 'if (myrefclass == nullptr)' in the code. At that point the compiler gives me an error complaining  about ambiguity between the two operator == I have implemented for my ref class. Naturally I want only one of the operator == to handle this case, preferable the first of the two. Short of the ugly casting of the nullptr to a ref class pointer to my ref class, so that the first of the two operator == is invoked, is there a more elegant way around this dilemma ?
    Sunday, February 18, 2007 12:09 AM

All replies

  • Since both form of your operator == taking reference parameters, the ambiguity is quite natural
    just do a static cast for specifying which operator == you meant to invoke. Do I need to post the code?
    Sunday, February 18, 2007 5:11 AM
  •  Sarath. wrote:
    Since both form of your operator == taking reference parameters, the ambiguity is quite natural
    just do a static cast for specifying which operator == you meant to invoke. Do I need to post the code?


    No, you do not need to post the code. I already said that the static cast, each time I use 'nullptr', is ugly. That is why I was looking for an alternative solution.

    When I do not use 'nullptr' the particular operator == gets resolved automatically and without ambiguity to the particular static operator based on the parameter type. Only when using 'nullptr' is there an ambiguity problem, since 'nullptr' is resolved to any ref class type by the compiler when looking for an operator match. Perhaps it would have been best to resolve 'nullptr' to no ref class type for the purposes of finding an operator match, so that no user defined operator was called when 'nullptr' is used. Or perhaps there should have been some kind of special notation whereby a user-defined operator says whether or not it wants to consider 'nullptr' a valid match for a ref class type parameter.
    Sunday, February 18, 2007 12:45 PM
  • Of course, the preference for pointer-like semantics for reference types greatly contributes to this ambiguity. Standard C++ does not overload operators for pointers, which is a good thing.

    Anyway, you may want to remove the == nullptr altogether. E.g.

    R^ r;

    if ( r ) { ... }

    and the operato bool can be overloaded (and will be prefered over the builtin conversion).

    -hg

    Sunday, February 18, 2007 2:39 PM
  •  Holger Grund wrote:

    Of course, the preference for pointer-like semantics for reference types greatly contributes to this ambiguity. Standard C++ does not overload operators for pointers, which is a good thing.

    Yes, I agree that it is a good thing that there are no overload operators for pointers in standard C++. Nonetheless, considering that in C++/CLI comparing a ref class to nullptr, ala 'if (someRefClass == nullptr)', is really not an attempt to invoke the equality operator but just a check to see whether the object has been gcnew'ed, perhaps such an exception could have been made in this case in the design of C++/CLI ( and the == and != operators in .Net in general ) so that the user-defined operator is never called..

     Holger Grund wrote:

    Anyway, you may want to remove the == nullptr altogether. E.g.

    R^ r;

    if ( r ) { ... }

    and the operato bool can be overloaded (and will be prefered over the builtin conversion).

    -hg



    Yes, this is a workaround to the problem but when one is developing components for end users, as I am, others are involved and I can not force others to use 'if (r)' instead of 'if ( r != nullptr)' etc.

    My own solution was to just limit the static operator == ( and != ) to its own type, which cuts down usability a little but avoids the 'if (someRefClass == nullptr)' ambiguity problem. Since the other ref class types I wanted to use this with can be converted easily enough to the actual type in which the static operators exist via constructors, I am basically OK.
    Sunday, February 18, 2007 7:03 PM
  • actually, your wish can be easily achieved by using a concrete template based overloading of the (in)equality operators.

     

    Instead of explaining anything, I'm just giving you a very simple sample to demonstrate it.  As you can see, == or != can be used to compare either regular objects or nullptr objects.

     

    #include "stdafx.h"

    using namespace System;

    ref struct A

    {

    public:

    int^ getValue() { return 5000; }

    };

    ref struct B : public A

    {

    public:

    };

    ref struct C : public B

    {

    public:

    int^ getValue() { return 5555; }

    };

    template<class A, class B>

    static bool operator==(A^ x, B^ y)

    {

    return x!=nullptr && y!=nullptr && (x->getValue())->Equals(y->getValue());

    }

    template<class A, class B>

    static bool operator!=(A^ x, B^ y)

    {

    return !(x==y);

    }

    int main(array<System::String ^> ^args)

    {

    //let's have 3 objects to boot

    A^ a = gcnew A;

    B^ b = gcnew B;

    C^ c = gcnew C;

    // display the value of a, b, c

    Console::WriteLine("a: {0}", a!=nullptr? a->getValue()->ToString() : "(null)");

    Console::WriteLine("b: {0}", b!=nullptr? b->getValue()->ToString() : "(null)");

    Console::WriteLine("c: {0}", c!=nullptr? c->getValue()->ToString() : "(null)");

    //compare a with a, b, c

    Console::WriteLine("a==a: {0}\ta!=a: {1}", a==a, a!=a);

    Console::WriteLine("a==b: {0}\ta!=b: {1}", a==b, a!=b);

    Console::WriteLine("a==c: {0}\ta!=c: {1}", a==c, a!=c);

    //make c a nullptr object

    c = nullptr;

    Console::WriteLine("c: {0}", c!=nullptr? c->getValue()->ToString() : "(null)");

    //compare a with nullptr, and nullptr with a

    Console::WriteLine("a==nullptr: {0}\ta!=nullptr: {1}", a==c, a!=c);

    Console::WriteLine("nullptr==a: {0}\tnullptr!=a: {1}", c==a, c!=a);

    return 0;

    }

     

    Here is the output:

     

    a: 5000
    b: 5000
    c: 5555
    a==a: True      a!=a: False
    a==b: True      a!=b: False
    a==c: False     a!=c: True
    c: (null)
    a==nullptr: False       a!=nullptr: True
    nullptr==a: False       nullptr!=a: True

     

    BTW, 1) the argument A and B don't have to have a relationship between them; 2) but they can be same types.

    Cheers.

    henry

    Tuesday, February 20, 2007 11:12 PM
  •  HCTwinJava wrote:

    actually, your wish can be easily achieved by using a concrete template based overloading of the (in)equality operators.

    Instead of explaining anything, I'm just giving you a very simple sample to demonstrate it. As you can see, == or != can be used to compare either regular objects or nullptr objects.

    #include "stdafx.h"

    using namespace System;

    ref struct A

    {

    public:

    int^ getValue() { return 5000; }

    };

    ref struct B : public A

    {

    public:

    };

    ref struct C : public B

    {

    public:

    int^ getValue() { return 5555; }

    };

    template<class A, class B>

    static bool operator==(A^ x, B^ y)

    {

    return x!=nullptr && y!=nullptr && (x->getValue())->Equals(y->getValue());

    }

    This should be:

    "template<class A, class B>

    static bool operator==(A^ x, B^ y)

    {

    if (x == nullprtr && y == nullptr) { return true;}

    return x!=nullptr && y!=nullptr && (x->getValue())->Equals(y->getValue());

    }"

    since you really want yur equality comparison to be true if both are nullptrs, else a statement like:

    "if (someA == nullptr)" will not work correctly

     HCTwinJava wrote:

    template<class A, class B>

    static bool operator!=(A^ x, B^ y)

    {

    return !(x==y);

    }

    int main(array<System::String ^> ^args)

    {

    //let's have 3 objects to boot

    A^ a = gcnew A;

    B^ b = gcnew B;

    C^ c = gcnew C;

    // display the value of a, b, c

    Console::WriteLine("a: {0}", a!=nullptr? a->getValue()->ToString() : "(null)");

    Console::WriteLine("b: {0}", b!=nullptr? b->getValue()->ToString() : "(null)");

    Console::WriteLine("c: {0}", c!=nullptr? c->getValue()->ToString() : "(null)");

    //compare a with a, b, c

    Console::WriteLine("a==a: {0}\ta!=a: {1}", a==a, a!=a);

    Console::WriteLine("a==b: {0}\ta!=b: {1}", a==b, a!=b);

    Console::WriteLine("a==c: {0}\ta!=c: {1}", a==c, a!=c);

    //make c a nullptr object

    c = nullptr;

    Console::WriteLine("c: {0}", c!=nullptr? c->getValue()->ToString() : "(null)");

    //compare a with nullptr, and nullptr with a

    Console::WriteLine("a==nullptr: {0}\ta!=nullptr: {1}", a==c, a!=c);

    Console::WriteLine("nullptr==a: {0}\tnullptr!=a: {1}", c==a, c!=a);

    return 0;

    }

    Here is the output:

    a: 5000
    b: 5000
    c: 5555
    a==a: True a!=a: False
    a==b: True a!=b: False
    a==c: False a!=c: True
    c: (null)
    a==nullptr: False a!=nullptr: True
    nullptr==a: False nullptr!=a: True

    BTW, 1) the argument A and B don't have to have a relationship between them; 2) but they can be same types.

    Cheers.

    henry



    This is a neat idea but will not work in my case since the class types are not all part of the same hierarchy with a common way of doing equality like your GetValue function in the example. However I will play around with partial specialization to see if I can get this to work. However I believe I will run into the same problem, since a nullptr can represent any T ^ for the compiler when resolving which template to use.
    Wednesday, February 21, 2007 11:49 AM