Zdravko Donev
Zdravko Donev

Reputation: 402

C++ overloading method based on derived class

I am facing the following problem with my code:

#include <iostream>

using namespace std;

class Base {
public:
    virtual void sayHello()=0;
};

class Impl1 : public Base {
public:
    void sayHello() { cout << "Hi from Impl1" << endl; }
};

class Impl2 : public Base {
public:
    void sayHello() { cout << "Hi from Impl2" << endl; }
};

void sayHello(Impl1 *i) {
    cout << "Impl1 says: ";
    i->sayHello();
}

void sayHello(Impl2 *i) {
    cout << "Impl2 says: ";
    i->sayHello();
}

int main()
{
    Impl1 *i1 = new Impl1();
    Base *b = i1;

    sayHello(b);

    return 0;
}

And here the compiler complains about the sayHello(b); line in the code.

"call of overloaded 'sayHello(Base*&)' is ambiguous"

Is there a way to solve this problem?

EDIT:

I basically want to pass my object to a function that does some calculations based on the type of the object. My object intentionally lacks of information in order to make the needed calculations. So Impl1 and Impl2 just contain some basic data, without the knowledge of more data needed to do the calculations.

Upvotes: 1

Views: 84

Answers (3)

songyuanyao
songyuanyao

Reputation: 172924

Overload resolution is performed at compile time. It means for sayHello(b);, the compiler only know that the type of b is Base*, it won't and can't know that b is pointing to a Impl1 object actually. Then results in ambiguous call; converting Base* to Impl1* or Impl2* is equivalent rank for the call.

PS: might be OT, but for you code sample, a function taking a Base* would work fine; dynamic dispach will take effect.

class Base {
public:
    virtual void sayHello()=0;
};

class Impl1 : public Base {
public:
    void sayHello() { cout << "Hi from Impl1" << endl; }
};

class Impl2 : public Base {
public:
    void sayHello() { cout << "Hi from Impl2" << endl; }
};

void sayHello(Base *i) {
    cout << "Some derived class of Base says: ";
    i->sayHello();
}

int main()
{
    Impl1 i1;
    Impl2 i2;

    Base *b = &i1;
    sayHello(b);   // "Hi from Impl1"
    b = &i2;
    sayHello(b);   // "Hi from Impl2"

    return 0;
}

If you need to know the dynamic type at run-time, you can use dynamic_cast. e.g.

Base *b = /* something */;
Impl1 * pi1 = dynamic_cast<Impl1*>(b);
if (pi1 != nullptr) sayHello(pi1);

Upvotes: 3

Christian Hackl
Christian Hackl

Reputation: 27528

Get rid of the two free-standing functions and call b->sayHello(); directly:

Impl1 *i1 = new Impl1();
Base *b = i1;
b->sayHello();

Or use an ugly workaround with dynamic_cast:

Impl1 *i1 = new Impl1();
Base *b = i1;
sayHello(dynamic_cast<Impl1*>(b));

The need to resort to dynamic_cast often suggests an error in the class design. This may very well be the case here. Chances are that you should never have introduced a supposedly object-oriented base class in the first place.


Note also that you do not call delete at the end. If you do, you will need a virtual destructor in Base.

Upvotes: 0

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726579

Since overloads are resolved at compile time, you have to supply the compiler with the exact type in order for the overload resolution to succeed.

In order to dispatch on the type, add a virtual member function to the Base, and use it to choose the overload:

class Base {
public:
    virtual void sayHello()=0;
    virtual void callSayHello() = 0;
};

class Impl1 : public Base {
public:
    void sayHello() { cout << "Hi from Impl1" << endl; }
    void callSayHello() {sayHello(this); }
};

class Impl2 : public Base {
public:
    void sayHello() { cout << "Hi from Impl2" << endl; }
    void callSayHello() {sayHello(this); }
};

void sayHello(Impl1 *i) {
    cout << "Impl1 says: ";
    i->sayHello();
}

void sayHello(Impl2 *i) {
    cout << "Impl2 says: ";
    i->sayHello();
}
...
b->callSayHello();

Note that implementations of callSayHello are identical, but you cannot place them into Base class, because the type of this would be different.

Note: the idea for this implementation is borrowed from C++ implementation of the Visitor Pattern.

Upvotes: 0

Related Questions