Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ambiguous conversion in dynamic_cast

Consider the problem of getting an object as argument and printing its type:

#include <iostream>

class A { };
class B : public A { };
class C : public A { };
class D : public C, public B { };

using namespace std;

template<class T>
void print_type(T* info)
{
    if(dynamic_cast<D*>(info))
        cout << "D" << endl;
    else if(dynamic_cast<C*> (info))
        cout << "C" << endl;
    else if(dynamic_cast<B*>(info))
        cout << "B" << endl;
    else if(dynamic_cast<A*> (info))
        cout << "A" << endl;
}

int main(int argc, char** argv)
{
    D d;
    print_type(&d);
    return 0;
}

It gives me the following error: "Ambiguous conversion from derived class 'D' to base class."
But I fail to see where's the ambiguity: if the object declared in main (d) is of type D, why can't be it directly converted to a type A?

Also, if I pass an argument of type string of course I get other errors:
'std::basic_string<char>' is not polymorphic

In Java for generics there is the syntax: <T extends A>; in this case it would be useful. How can I make a similar thing in C++ with templates?


I have modified the code this way:

#include <iostream>
#include <vector>

class A { };
class B : virtual public A { };
class C : virtual public A { };
class D : public C, public B { };

using namespace std;

template<class T>
void print_type(T* info)
{
    if(dynamic_cast<D*>(info))
        cout << "D" << endl;
    else if(dynamic_cast<C*> (info))
        cout << "C" << endl;
    else if(dynamic_cast<B*>(info))
        cout << "B" << endl;
    else if(dynamic_cast<A*> (info))
        cout << "A" << endl;
}

int main(int argc, char** argv)
{
    string str;
    print_type(&str);
    return 0;
}

But I still get the error: 'std::basic_string<char>' is not polymorphic

like image 817
Ramy Al Zuhouri Avatar asked Apr 05 '12 23:04

Ramy Al Zuhouri


People also ask

Is an ambiguous base of CPP?

When you derive classes, ambiguities can result if base and derived classes have members with the same names. Access to a base class member is ambiguous if you use a name or qualified name that does not refer to a unique function or object.

Is dynamic_cast a code smell?

Yes, dynamic_cast is a code smell, but so is adding functions that try to make it look like you have a good polymorphic interface but are actually equal to a dynamic_cast i.e. stuff like can_put_on_board .

What is dynamic_cast operator used for?

The primary purpose for the dynamic_cast operator is to perform type-safe downcasts. A downcast is the conversion of a pointer or reference to a class A to a pointer or reference to a class B , where class A is a base class of B .

Why do we use dynamic cast conversion?

In C++, dynamic casting is mainly used for safe downcasting at run time. To work on dynamic_cast there must be one virtual function in the base class. A dynamic_cast works only polymorphic base class because it uses this information to decide safe downcasting.


2 Answers

First of all, this is not a templates problem. If you remove the template and just have print_type take a D*, you'll see that the error will still be there.

What is happening is you do not use virtual inheritance, hence you get this situation:

A   A
|   | 
B   C
 \ /
  D

The dynamic_cast doesn't know which A you are refering to.

To achieve this: (and I assume it's what you wanted)

  A
 / \
B   C
 \ /
  D

...you should use virtual inheritance, ergo:

class A
{
};

class B : virtual public A
{
};

class C : virtual public A
{
};

class D : public C,public B
{
};

... and now it compiles without problems :) (keep in mind that Virtual Inheritance Is Evil though)

like image 124
Kornel Kisielewicz Avatar answered Sep 23 '22 21:09

Kornel Kisielewicz


This is called a deadly diamond of death, or simply, diamond problem. The "path" to A can go through either B or C, hence a potential contradiction.

Furthermore, the idea of a template is to make it generic, not type aware. A template is not in itself compiled code, it's compiled against its use. It's a lot like a big macro.

like image 34
MPelletier Avatar answered Sep 23 '22 21:09

MPelletier