1

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.

1
  • You have no sayHello function that takes a Base * as an argument. Commented Nov 19, 2016 at 11:39

3 Answers 3

3

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);
Sign up to request clarification or add additional context in comments.

6 Comments

So how can I do this conversation at runtime? 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.
@ZdravkoDonev So you need to know the actual derived type inside the function? You might need dynamic_cast.
So you are proposing the: if(dynamic_cast<Impl1*>(b)) { } else if(dynamic_cast<Impl2*>(b)) { } else { // "who-knows" type } solution?
@ZdravkoDonev You could do that, but it's not a good idea. The 1st code sample shown in my answer would be better; don't depend on the actual derived types, depend on interfaces(i.e. Base class).
Yes, but my derived class doesn't contain the data needed to do the calculations and I don't want to pass objects of different classes, because I will get a spaghetti code. What should I do in this case?
|
0

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.

Comments

0

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.

3 Comments

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.
@ZdravkoDonev: I've added an alternative workaround.
Yes I thought of if-else if and dynamic_cast, but it doesn't seem a good idea. And maybe you are right about the problem of introducing a base class. But in some part of my code I need to treat my implementations as one base class, and in other I need to distinguish them.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.