0

I currently have the following structure

class A

class B : public A

class C : public A

I have virtual methods defined in A and B and C are overriding them. The methods are of the sort

bool C::CheckCollision(shared_ptr<B> box);

bool B::CheckCollision(shared_ptr<C> triangle);

I also have a vector of shared_ptr<A> in which I store all the game objects. The problem is that i cannot do the following

for (int i = 0; i < objects.size(); i++) { for (int j=i; j < objects.size(); j++ { objects[i]->CheckCollision(objects[j]); } } 

I get an error saying that the argument list does not match the overloaded function. Makes sense as I am trying to pass shared_ptr<A> where i am expecting shared_ptr<B> or shared_ptr<C>, but how do I go around this issue ? Is there a another way to do it ?

9
  • 2
    You cannot go around this. What if CheckCollision() uses something that's available only in B or C and you'll pass a std::shared_ptr<A> that will point to A object? You need to rethink your design. Commented Mar 3, 2019 at 12:22
  • makes sense, something else that comes to mind is that I can use an enum to store the type info and do a virtual GetType function. I know that you are not aware of the whole situation, but does that sound bad ? Commented Mar 3, 2019 at 12:26
  • To me, enum (preferably enum class) that holds a type smells too much C, but sometimes it's the right tool. If you truly want polymorphism, you need to stick virtual somewhere. Do note that it is possible to do a type check using dynamic_cast, but it's rarely a desired solution (not only it is slow, but it adds a lot of noise to the code). Commented Mar 3, 2019 at 12:31
  • 2
    I would think that CheckCollision would belong on A and then it would call virtual functions - like maybe BoundingBox() - to get the info it needs to run the collision detection algorithm. Commented Mar 3, 2019 at 12:38
  • You have virtual CheckCollision(shared_ptr<B> box) and virtual CheckCollision(shared_ptr<C> box), declared in your A class? Because if so then the design is definitely broken. A should not know something about B and C. If you don't have virtual CheckCollision(shared_ptr<B> box) and virtual CheckCollision(shared_ptr<C> box) in A, then you don't have a function you override. Commented Mar 3, 2019 at 12:40

1 Answer 1

5

Let's make it work with virtual functions and shared pointer to base class

First of all, you can perfectly make polymorphism work, using shared pointers to the base. Here a small snippet to show you how you could do it:

class A { public: virtual void show() { cout<<"A"<<endl; } virtual void collide(shared_ptr<A> a) { cout<<"collide A with "; a->show(); } virtual ~A() {} }; class B : public A { public: void show() override { cout<<"B"<<endl; } void collide(shared_ptr<A> a) override { cout<<"collide B with "; a->show(); } }; class C : public A { public: void show() override { cout<<"C"<<endl; } void collide(shared_ptr<A> a) override { cout<<"collide C with "; a->show(); } }; 

Your double loop would then look like:

vector<shared_ptr<A>> objects; objects.push_back (make_shared<A>()); // populate for the sake of demo objects.push_back (make_shared<B>()); objects.push_back (make_shared<C>()); for (int i = 0; i < objects.size(); i++) { objects[i]->show(); for (int j=i; j < objects.size(); j++) { objects[i]->collide(objects[j]); // note that you have to use -> not . } } 

Now, you see, in order to master the combinations, I used an override that knows the real type of its own object but doesn't know anything specific about the real type of the partner object. So to work out which kind of pairing it is, I need to invoke a polymorphic function of the partner object.

Online demo

The more general approach to your problem is double dispatch

This small proof of concept is to show a simple example. It's ideal when the problem can be decomposed in each of the partner object doing one part of the problem. But things are not always as simple, so you could find more elaborate techniques by googling for double dispatch. Fortunately the example of the collision is quite common.

Here another demo that uses a combination of overriding and overloading. I think this is the kind of things you try to achieve but solves it through one level of indirection more. It is inspired by the visitor pattern: The polymorphic collision function of an object is invoked with a shared pointer to the base class of the partner object. But the implementation of this function immediately calls a polymorphic function of the partner object with as argument a reference to itself (i.e. the knowledge of the real type of the argument, allows the compiler to select the right overload). This kind of "bounce-back" approach (by the way, it's a kind of inverted visitor) unfortunately requires the base class to know all its potential derived class, which is far from ideal. But it allows to offer a different behavior for every possible combination.

Another approach to double dispatch is to use a dispatch table. THis works by managing a kind of virtual table but with two types, and having some lookup for invoking the right function for the right combination.

Sign up to request clarification or add additional context in comments.

1 Comment

thank you for the explanation and the links, ill have a look at them :)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.