0

This may be a stupid question, I suspect I know the answer (no) because I seem to be hitting a wall here.

Given I have a collection of objects derived from certain class:

class BaseClass;
class DerivedA: public BaseClass;
class DerivedB: public BaseClass;
class DerivedC: public BaseClass;
std::vector<BaseClass> myCollection;

I want to call a method depending on the types of the specific class:

class Processor {
  void doSomething(DerivedA a, DerivedB b);
  void doSomething(DerivedA a, DerivedC c);
}

The problem is, if I access the individual items on the collection and try to call the 'doSomething' method in the 'Processor', it will not be able do decide which method to use (afaik). So my question is: Is there any way to fetch the items in the collection with the right derived-type?

uorbe001
  • 187
  • 3
  • 10
  • 3
    If you want derivation support (and polymorphism that comes with it). your vector should be of pointers (and smart-pointers at that); not base class instances. – WhozCraig Dec 27 '12 at 14:57
  • You may also be interested in the [visitor pattern](http://en.wikipedia.org/wiki/Visitor_pattern) – Tom Knapen Dec 27 '12 at 15:41
  • @WhozCraig Thanks for the heads-up, Karthik T explained why I'd need this and I didn't realize object-slicing would occur if I use instances. – uorbe001 Dec 27 '12 at 22:56
  • Following is an example of multiple dispatch in C++11: http://ideone.com/lTsc7M – Jarod42 Feb 04 '14 at 16:26

2 Answers2

2

If you are going to keep the doSomething method as it is, this is what is called multiple dispatch and is NOT currently supported by C++.

If it were a virtual member function of BaseClass then yes it would be the run of the mill C++ polymorphism on the object it is being invoked on, but it would still NOT automatically infer the type of the arguement.

To get around this you can do something like what is suggested in the earlier link

void collideWith(Thing& other) {
     // dynamic_cast to a pointer type returns NULL if the cast fails
     // (dynamic_cast to a reference type would throw an exception on failure)
     if (Asteroid* asteroid = dynamic_cast<Asteroid*>(&other)) {
         // handle Asteroid-Asteroid collision
     } else if (Spaceship* spaceship = dynamic_cast<Spaceship*>(&other)) {
         // handle Asteroid-Spaceship collision
     } else {
         // default collision handling here
     }
 }

Basically keep casting to various possible Derived classes until one works and call one of the methods appropriately(no special effort since the compiler knows what type you are trying to cast to).

IMPORTANT: as @WhozCraig points out, your vector needs to hold pointers to avoid Object-Slicing and render this whole question moot.

Community
  • 1
  • 1
Karthik T
  • 31,456
  • 5
  • 68
  • 87
  • It's not as pretty as I'd like it to be, but it looks like this is the best option in order to do what I want. Thanks for explaining @WhozCraig's comment too, I didn't realize why he said that until I read your answer :-) – uorbe001 Dec 27 '12 at 23:00
  • @uorbe001 Sorry I didn't elaborate particularly well in that comment, but Karthik filled the hole I left rather nicely in this answer. Glad you see how it ties together. – WhozCraig Dec 28 '12 at 00:58
1

Ok, yes you should use polymorphism as the above stated. If your function needs to handle 2 objects though it gets extremely complicated.

If the derivations form a limited set and know each other you can use double-dispatch. It's not perfect but it solves this particular case.

class DerivedA;
class DerivedB;
class DerivedC;

class BaseClass
{
 public:
     virtual ~BaseClass();

     virtual void doSomethingWithBase( BaseClass & b2 ) = 0;
     virtual void doSomethingWithDerivedA( DerivedA & da ) = 0;
     virtual void doSomethingWithDerivedB( DerivedB & db ) = 0;
     virtual void doSomethingWithDerivedC( DerivedC & dc ) = 0;
};

class DerivedA : public BaseClass
{
   public:

      void doSomethingWithBase( BaseClass & b2 )
      {
           b2.doSomethingWithDerivedA( *this );
      }

      void doSomethingWithDerivedA( DerivedA & da )
      {
           // implement for two DerivedA objects
      }

      void doSomethingWithDerivedB( DerivedB & db )
      {
           // implement for an A and B
      }

      void doSomethingWithDerivedC( DerivedC & dc )
      {
          // implement for an A and C
      }
 };

 // implement DerivedB to call doSomethingWithDerivedB on its parameter
 // implement DerivedC to call doSomethingWithDerivedC on its parameter.

You get the idea. From where you call you don't need to know which two types you have and you never need to actually look this up. But if you ever add more implementations you have a lot of code to edit and may consider some kind of lookup table.

If you need a class to define itself you can use some kind of virtual id.

  class BaseClass
  {
      public:
         virtual int id() const = 0;
  };

and then you get the classes to reveal their ids and find the handler in the table based on these ids that wil handle the two objects. The ids don't have to be ints, they can be strings which makes it easier to avoid naming clashes, and this has the advantage over the double-dispatch method of the base class not knowing its derived classes or them knowing each other, and being extensible. You also don't have to handle every pair.

CashCow
  • 30,981
  • 5
  • 61
  • 92
  • I did consider a double dispatch, but it wouldn't work if I want to keep the logic in a separate class (in this case, I do). In theory, you could implement a tripple dispatch as far as I know, but that would cause more trouble than it's worth :-( – uorbe001 Dec 27 '12 at 22:53