2

Assume we have an abstract class called Vehicle:

class Vehicle {
    virtual bool raceWith(Vehicle *anotherVehicle) = 0;
};

And we have its subclasses Bicycle and Car:

//  forward declaration
class Car;

class Bicycle : public Vehicle {
    virtual bool raceWith(Vehicle *anotherVehicle) {
        throw SomeExceptionClass();
    }

    virtual bool raceWith(Car *anotherVehicle) {
        return true;
    }

    virtual bool raceWith(Bicycle *anotherVehicle) {
        return false;
    }
};

However, this block of code throws SomeExceptionClass:

Vehicle *aBicycle = new Bicycle();
Vehicle *aCar = new Car();

aBicycle->raceWith(aCar);

What to do here? Does not C++ allow us to use polymorphic methods in this manner?

Any help will be appreciated. Thanks.

EDIT: It will be also good to provide an answer with dynamic_cast<> and decltype variants?

Buğra Ekuklu
  • 3,049
  • 2
  • 17
  • 28

3 Answers3

4

The following will race a Bicycle with a Vehicle:

Vehicle *aBicycle = new Bicycle();
Vehicle *aCar = new Car();

aBicycle->raceWith(aCar);

To race a Bicycle with a Car we need to use double dispatch.

To make double dispatch work we use the this pointer to call the next function so that the second virtual call can resolve to the correct type of vehicle:

class Car;
class Bicycle;

class Vehicle {
public:
    virtual bool raceWith(Vehicle& anotherVehicle) = 0;
    virtual bool raceWith(Bicycle& anotherVehicle) = 0;
    virtual bool raceWith(Car& anotherVehicle) = 0;
};


class Bicycle : public Vehicle {
public:
    virtual bool raceWith(Vehicle& anotherVehicle) override
    {
        //throw std::exception();
        return anotherVehicle.raceWith(*this);
    }

    virtual bool raceWith(Car& anotherVehicle)
    {
        return true;
    }

    virtual bool raceWith(Bicycle& anotherVehicle)
    {
        return false;
    }
};

class Car : public Vehicle {
public:
    virtual bool raceWith(Vehicle& anotherVehicle) override
    {
        return true;
    }

    virtual bool raceWith(Car& anotherVehicle) override
    {
        return true;
    }

    virtual bool raceWith(Bicycle& anotherVehicle) override
    {
        return false;
    }

};

int main()
{

    Vehicle *aBicycle = new Bicycle();
    Vehicle  *aCar = new Car();

    aBicycle->raceWith(*aCar);
}

Note the return anotherVehicle.raceWith(*this); which will perform the second virtual call.

The functions are then called in this order:

  • main();
  • Bicycle::raceWith(Vehicle& anotherVehicle);
  • Car::raceWith(Bicycle& anotherVehicle);

The following is the same program, but sticking with the use of pointers and exceptions as provided in the question:

#include <exception>

class Car;
class Bicycle;

class Vehicle {
public:
    virtual bool raceWith(Vehicle* anotherVehicle) = 0;
    virtual bool raceWith(Bicycle* bicycle) = 0;
    virtual bool raceWith(Car* car) = 0;
};


class Bicycle : public Vehicle {
public:
    virtual bool raceWith(Vehicle* anotherVehicle) override
    {
        //throw std::exception();
        return anotherVehicle->raceWith(this);
    }

    virtual bool raceWith(Car* car) override
    {
        return true;
    }

    virtual bool raceWith(Bicycle* bicycle) override
    {
        return false;
    }
};

class Car : public Vehicle {
public:
    virtual bool raceWith(Vehicle* anotherVehicle) override
    {
        throw std::exception();
    }

    virtual bool raceWith(Car* car) override
    {
        return true;
    }

    virtual bool raceWith(Bicycle* bicycle) override
    {
        return false;
    }

};

int main()
{

    Vehicle *aBicycle = new Bicycle();
    Vehicle  *aCar = new Car();

    aBicycle->raceWith(aCar);
}
wally
  • 10,717
  • 5
  • 39
  • 72
  • What is double dispatch? – Buğra Ekuklu Apr 21 '16 at 18:31
  • 1
    @Leviathlon double virtual dispatching is the ability to call the correct implementation depending on the dynamic type of **two** objects. C++ only provides single virtual dispatching, on `this`. What flatmouse describes here is known as the Visitor pattern, and uses single dispatching twice to achieve double dispatching. – Quentin Apr 21 '16 at 19:19
  • This solved the problem. Such a great answer. Thank you for attention. – Buğra Ekuklu Apr 21 '16 at 19:31
1

By doing

Vehicle *aBicycle = new Bicycle();
aBicycle->raceWith(aCar);

The type of aCar is Vehicle (typeid(aCar).name()), so your compiler is calling the method with Vehicle.

Try

aBicycle->raceWith(static_cast<Car*>(aCar));
aBicycle->raceWith(dynamic_cast<Car*>(aCar));

As for why static_cast or dynamic_cast, you can look at this post on SO: Regular cast vs. static_cast vs. dynamic_cast

Community
  • 1
  • 1
Ceros
  • 491
  • 3
  • 14
1

Try this, you can use dynamic cast to detect the type for subclass. If you keep virtual bool raceWith(Vehicle *anotherVehicle) as a method, probably virtual bool raceWith(Car *anotherVehicle) and virtual bool raceWith(Bicycle *anotherVehicle) will never get executed without casting since anotherVehicle is a Vehicle type obj. Hope will help :)

#include <iostream>
using namespace std;
class Car;
class Bicycle;

class Vehicle {
    public:
     //virtual bool raceWith(Vehicle *anotherVehicle) = 0;
     virtual bool raceWith(Car *anotherVehicle) = 0;
     virtual bool raceWith(Bicycle *anotherVehicle) = 0;
};

class Bicycle : public Vehicle {
    /*virtual bool raceWith(Vehicle *anotherVehicle) {
        throw SomeExceptionClass();
    }*/
    virtual bool raceWith(Car *anotherVehicle) {
        cout << "will print" << endl;
        return true;
    }
    virtual bool raceWith(Bicycle *anotherVehicle) {
        return false;
    }
};
class Car : public Vehicle {
    /*virtual bool raceWith(Vehicle *anotherVehicle) {
        throw SomeExceptionClass();
    }*/
    virtual bool raceWith(Car *anotherVehicle) {
        return true;
    }

    virtual bool raceWith(Bicycle *anotherVehicle) {
        return false;
    }
};
int main()
{
    Vehicle *aBicycle = new Bicycle();
    Vehicle *aCar = new Car();

    if (dynamic_cast<Bicycle*>(aCar) != NULL) {
        std::cout << "Race with A Bicycle" << std::endl;
        aBicycle->raceWith(static_cast<Bicycle*>(aCar));
    }
    else if (dynamic_cast<Car*>(aCar) != NULL) {
        std::cout << "Race with A Car" << std::endl;
        aBicycle->raceWith(static_cast<Car*>(aCar));
    }
    else {
        //throw SomeExceptionClass();
    }

    //aBicycle->raceWith(aCar);

    return 0;
}
JaNL
  • 99
  • 1
  • 5
  • I think, for what you are trying to achieve here, google -Visitor design pattern- will help you more – JaNL Apr 21 '16 at 19:32