2012-04-05 31 views
4

Betrachten Sie das Problem ein Objekt als Argument bekommen und den Druck seiner Art:Mehrdeutige Umwandlung in dynamic_cast

#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; 
} 

Es gibt mir die folgende Fehlermeldung: "Mehrdeutige Umwandlung von abgeleiteten Klasse‚D‘auf der Basisklasse. "
Aber ich sehe nicht, wo die Mehrdeutigkeit ist: Wenn das in main (d) deklarierte Objekt vom Typ D ist, warum kann es nicht direkt in einen Typ A konvertiert werden?

Auch, wenn ich ein Argument vom Typ String natürlich passieren erhalte ich Fehler:
'std::basic_string<char>' is not polymorphic

In Java für Generika gibt es die Syntax: <T extends A>; in diesem Fall wäre es nützlich. Wie kann ich eine ähnliche Sache in C++ mit Vorlagen machen?


Ich habe den Code auf diese Weise geändert:

#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; 
} 

Aber ich bekomme immer noch den Fehler: 'std::basic_string<char>' is not polymorphic

+0

Ich nehme an, dies ist nur ein anschauliches Beispiel, kein realer Code? Das ist natürlich genau das, was mit Polymorphie gelöst wird. –

+0

Es ist nicht illustrativ, aber ich habe diesen Code ausprobiert, um zu sehen, wie dynamic_cast funktioniert. –

+1

@RamyAlZuhouri Siehe [MSDN] (http://msdn.microsoft.com/en-us/library/4k5yex0s%28v=vs.71%29.aspx) darüber. "Sie können dynamic_cast nicht verwenden, um von einer nicht-polymorphen Klasse (eine Klasse ohne virtuelle Funktionen) zu konvertieren." – MPelletier

Antwort

1

Consider the problem of getting an object as argument and printing it's type:

Seufz ... RTTI verwenden.

#include <iostream> 
#include <string> 
#include <typeinfo> 

template<class T> void print_type(const T& info){ 
    std::cout << typeid(info).name() << std::endl; 
} 

int main(int argc, char** argv){ 
    D d; 
    int a = 3; 
    std::string test("test"); 
    print_type(d); 
    print_type(a); 
    print_type(test); 
    return 0; 
} 
3

Das nennt man eine deadly diamond of death oder einfach, Diamant Problem. Der "Pfad" zu A kann entweder B oder C durchlaufen, daher ein möglicher Widerspruch.

Darüber hinaus ist die Idee einer Vorlage, um es generisch, nicht typbewusst zu machen. Eine Vorlage ist kein in sich kompilierter Code, sie ist gegen ihre Verwendung kompiliert. Es ist viel wie ein großes Makro.

+1

Virtuelle Vererbung löst dies. Google es, wenn Sie neugierig sind. Hier ist der Wikipedia-Artikel dazu: http://en.wikipedia.org/wiki/Virtual_inheritance –

+1

@trinithis: zu sagen, virtuelle Vererbung "löst das" ist irreführend - das Design wie gezeigt hat zwei A-Objekte und kann sie gut brauchen .. Das Ändern, um ein gemeinsames A-Objekt zu haben, kann oder kann nicht lebensfähig sein. –

+0

Es ist kein Diamant, weil nur zwei A-Klassen erstellt wurden. –

6

Zunächst ist dies kein Template-Problem. Wenn Sie die Vorlage entfernen und einfach print_type eine D* nehmen, werden Sie sehen, dass der Fehler immer noch da ist.

Was passiert ist, dass Sie virtuelle Vererbung nicht Gebrauch machen, damit Sie diese Situation erhalten:

A A 
| | 
B C 
\/
    D 

Die dynamic_cast nicht kennt dieA Sie sich beziehen auf.

Um dies zu erreichen: (und ich nehme an, es ist das, was man wollte)

A 
/\ 
B C 
\/
    D 

... Sie virtual inheritance, ergo verwenden sollte:

class A 
{ 
}; 

class B : virtual public A 
{ 
}; 

class C : virtual public A 
{ 
}; 

class D : public C,public B 
{ 
}; 

... und jetzt ist es kompiliert ohne Probleme :) (Denken Sie daran, dass Virtual Inheritance ist böse, aber)

1

Der Fehler erklärt es eigentlich ziemlich gut. "Mehrdeutige Konvertierung" Das Problem ist nicht, dass es keine D* zu einem A* werfen kann. Das Problem ist, dass D* kann zu einer von 2 verschiedenen A* s gegossen werden.Ein guter Weg, um zu sehen, was das Problem ist, ist es, einen einfachen Wert in A zu setzen, wie:

class A 
{ 
    public: 
    int value; 
} 

nun in B 's Konstruktor gesetzt A.value-1 und in C' s Konstruktor gesetzt A.value zu 2. D hat jetzt ein A mit einem Wert von 1 und ein anderes A mit einem Wert von 2. Was die Frage zwingt: Wenn du deine D* zu einer A* umwandeln willst, welche willst du?