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
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.
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 .
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 .
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.
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)
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With