virtual == operator

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Floogle

    virtual == operator

    how do i create a virtual == operator. I've tried the following but
    it's incorrect...

    class Interface
    {
    ...

    public:

    virtual bool operator==(cons t Interface& rhs)const=0;
    };

    class MyClass : public Interface
    {
    ...

    public:

    bool operator==(cons t MyClass& rhs)const;
    };

    thanks
  • Atlas

    #2
    Re: virtual == operator


    Floogle wrote:[color=blue]
    > how do i create a virtual == operator. I've tried the following but
    > it's incorrect...
    >
    > class Interface
    > {
    > ...
    >
    > public:
    >
    > virtual bool operator==(cons t Interface& rhs)const=0;
    > };
    >
    > class MyClass : public Interface
    > {
    > ...
    >
    > public:
    >
    > bool operator==(cons t MyClass& rhs)const;
    > };
    >
    > thanks[/color]

    operator== should be declared friend to keep its intuitive behaviour.
    friend bool operator==(cons t Interface& d1,const Interface& d2);
    It can't be virtual, then.
    But it doesn't matter, because the parameter is reference, so you can
    still use dynamic binding by supplying both ends with
    MyClassObj1==My ClassObj2. You don't need to define it in MyClass.

    Comment

    • Victor Bazarov

      #3
      Re: virtual == operator

      Floogle wrote:[color=blue]
      > how do i create a virtual == operator. I've tried the following but
      > it's incorrect...
      >
      > class Interface
      > {
      > ...
      >
      > public:
      >
      > virtual bool operator==(cons t Interface& rhs)const=0;
      > };
      >
      > class MyClass : public Interface
      > {
      > ...
      >
      > public:
      >
      > bool operator==(cons t MyClass& rhs)const;
      > };[/color]

      The argument has to be of the same type. Inside you can dynamic_cast it
      to MyClass const&, and catch the exception if it's not of MyClass type,
      and return false in that case, probably.

      V

      Comment

      • Mirek Fidler

        #4
        Re: virtual == operator

        > The argument has to be of the same type. Inside you can dynamic_cast it[color=blue]
        > to MyClass const&, and catch the exception if it's not of MyClass type,
        > and return false in that case, probably.[/color]

        Well, maybe dynamic_cast of pointer and testing for NULL is cheaper, is
        not it?

        Mirek

        Comment

        • Victor Bazarov

          #5
          Re: virtual == operator

          Mirek Fidler wrote:[color=blue][color=green]
          >> The argument has to be of the same type. Inside you can dynamic_cast it
          >> to MyClass const&, and catch the exception if it's not of MyClass type,
          >> and return false in that case, probably.[/color]
          >
          >
          > Well, maybe dynamic_cast of pointer and testing for NULL is cheaper, is
          > not it?[/color]

          Unless it's proven to be different (and actually affecting the program's
          performance), I am not going to guess. Neither should anyone else.

          V

          Comment

          • Kaz Kylheku

            #6
            Re: virtual == operator


            Floogle wrote:[color=blue]
            > how do i create a virtual == operator. I've tried the following but
            > it's incorrect...[/color]

            The short answer is that you use an more powerful object-oriented
            programming system in which a virtual function is dispatched by
            considering the dynamic types of *all* of the specializable arguments.

            There is a crutch design pattern that you can use in a less powerful
            object system, like that of C++, to emulate multiple dispatch. You end
            up making two virtual function calls.

            The first virtual call dynamically dispatches on the left object, and
            goes to a stub function, whose only purpose is to dispatch one more
            time on the right object.

            This is done in the ``Visitor Pattern'' for instance. The problem with
            that pattern is that it uses generic terminology like ``visit'' and
            ``accept'' which obscures the semantics of what the user is actually
            implementing. You can rip out the double dispatch trick, without
            taking in the whole pattern in.

            [color=blue]
            > class Interface
            > {
            > ...
            >
            > public:
            >
            > virtual bool operator==(cons t Interface& rhs)const=0;
            > };
            >
            > class MyClass : public Interface
            > {
            > ...
            >
            > public:
            >
            > bool operator==(cons t MyClass& rhs)const;
            > };[/color]

            Of course, the function you have here in MyClass is not an overload of
            the base class virtual function, because the type signature does not
            match. You must in fact implement:

            bool operator==(cons t Interface &rhs) const;

            So now, the problem is that this dispatches only on the type of the
            object on which the virtual is called. You know that your ``this''
            pointer is a MyClass, but you need to handle all combinations of
            MyClass and everything else. The trick is to invoke another virtual
            function, this time on the rhs object:

            bool MyClass::operat or==(const Interface &rhs) const
            {
            return rhs.operator == (*this);
            }

            In this second virtual call, the arguments are reversed: the parameter
            is now const MyClass & and static overload resolution is being used to
            find the method. That's because we know the exact type of the left hand
            side object!

            All you need now is additional virtual functions inside Interface which
            are specialized to various types of objects.

            // Inside Interface base:
            virtual bool operator==(cons t MyClass& rhs) const = 0;
            virtual bool operator==(cons t YourClass &rhs) const = 0;

            // .. etc ... for every darn class! Every time you add a class
            // to your framework, you have to add an entry here, and
            // implement the combination throughout the entire framework!!!

            So for instance the combination MyClass X MyClass -> bool is handled by
            writing an additional function in MyClass:

            bool MyClass::operat or == (const MyClass &lhs) const
            {
            }

            and the YourClass X MyClass -> bool combination is handled like this:

            // You HAVE to implement this because it's a pure virtual
            // inside the Interface base!!!

            bool MyClass::operat or == (const YourClass &lhs) const
            {
            // handle the combination here.
            }

            and so on. I'm calling it lhs because the order is reversed; we are at
            the second dispatch level, where we invoked the virtual function on the
            right hand side object in the original ==() call! The original left
            hand object is now the argument.

            One thing you might want to do is use a different name for the two
            steps, like in the visitor pattern, which has visit() for the first
            call and accept() for the other. Confusion can occur because some of
            the == functions can be called non-virtually, when you aren't going
            through base classes. In this case, it should all be cool because the
            comparison is commutative (right?)

            That is to say, if you have two MyClass objects and you compare them
            with ==, then it will just go to the operator == (const MyClass &)
            right away without the double dispatch, and the right hand side will be
            assumed to be the left.

            To avoid that, do it like this:

            class Interface {
            public:
            // entry point into comparison
            virtual bool operator == (const Interface &rhs) const = 0;

            // second dispatch completion routines, distinct from operator
            virtual bool eq_impl(const MyClass &from_lhs) const = 0;
            virtual bool eq_impl(const YourClass &from_lhs) const = 0;
            // repeat for every darn class, implement all combos
            };

            The operator == implementation is the same everywhere:

            return rhs.eq_impl(*th is);

            everyone must implement this. Everyone must also implement every
            eq_impl for every left hand class.

            This could be extended to triple dispatch:

            virtual void func(Interface &B, Interface &C) = 0;

            Let's refer to the first object as the hidden parameter ``A'', so the
            manifest parameters are B and C.

            At the first level, the type of the object is established. So now, it
            can invoke a second level virtual, invoked on Interface B. The ``A''
            object now appears as a concrete parameter with an exact class. C
            remains abstract:

            virtual void func2(Concrete &A, Interface &C) = 0;

            here, the exact type of A and B is known, so a final virtual call can
            take place on object C, which statically chooses a virtual based on
            these two types:

            virtual void func3(Concrete &A, Concrete &B) = 0;

            Note that if you have M implementations of the interface, then you need
            M implementations of func(), M * M implementations of func2(), and M *
            M * M implementations of func3().

            Probably a good idea to make some of these impure, so you can inherit
            default behaviors and not have to deal with all the combos.

            Comment

            • Kaz Kylheku

              #7
              Re: virtual == operator

              Mirek Fidler wrote:[color=blue][color=green]
              > > The argument has to be of the same type. Inside you can dynamic_cast it
              > > to MyClass const&, and catch the exception if it's not of MyClass type,
              > > and return false in that case, probably.[/color]
              >
              > Well, maybe dynamic_cast of pointer and testing for NULL is cheaper, is
              > not it?[/color]

              That depends on how exception handling is implemented, how frequently
              the exceptional case occurs in your program, and at what level you
              catch it.

              Comment

              • Ali Çehreli

                #8
                Re: virtual == operator

                "Floogle" <BananaSplits@N OSPAM.com> wrote in message
                news:qnmfl11pgj i4q30l5hn1f70ag oimqscitn@4ax.c om...
                [color=blue]
                > how do i create a virtual == operator.[/color]

                A search on Google groups proved that in the past, I've responded to such a
                question like this :)

                <quote>
                In the past, I've used a variation of that method for operator<, but
                it should work for operator== as well.

                Define 'bool operator==(B const & other) const;' in the base class but
                call a private pure virtual isEqual member when the types are the
                same:

                if (typeid(*this) == typeid(other))
                {
                return isEqual(other);
                }
                else
                {
                return false;
                }

                In the most derived classes, you can trust the typeid check performed
                in operator==, and do a static_cast in isEqual:

                class D
                {
                int member_;

                virtual bool isEqual(Base const & o) const
                {
                Derived const & other = static_cast<Der ived const &>(o);

                return member_ == other.member_;
                }
                /* ... */

                };

                I couldn't use a compiler to test this code. I hope there aren't many
                errors :)

                Ali
                </quote>

                I think today I would write Base::operator= = in a shorter way:

                bool Base::operator= = (Base const & other)
                {
                return ((typeid(*this) == typeid(other)) &&
                isEqual(other)) ;
                }

                Ali

                Comment

                • Mirek Fidler

                  #9
                  Re: virtual == operator

                  Kaz Kylheku wrote:[color=blue]
                  > Mirek Fidler wrote:
                  >[color=green][color=darkred]
                  >>>The argument has to be of the same type. Inside you can dynamic_cast it
                  >>>to MyClass const&, and catch the exception if it's not of MyClass type,
                  >>>and return false in that case, probably.[/color]
                  >>
                  >>Well, maybe dynamic_cast of pointer and testing for NULL is cheaper, is
                  >>not it?[/color]
                  >
                  >
                  > That depends on how exception handling is implemented, how frequently
                  > the exceptional case occurs in your program, and at what level you
                  > catch it.[/color]

                  Well, I believe that:

                  - test for type has to be performed in both cases (I mean both for
                  pointer and reference)

                  - raising exception will cost you something no matter what

                  - in this case, the place where you catch the exception is in the same
                  function, so no possible saveings from passing multiple frames here

                  On some systems the difference might be small, but I do not believe that
                  in this particular case there is platform where exception based solution
                  would be faster. On many current platforms, it will be significantly slower.

                  Plus, it is more complex and more verbose code as well.

                  Mirek

                  Comment

                  Working...