2012-04-05 2 views
4

Рассмотрит проблему получения объекта в качестве аргумента и печати его типа:Неоднозначность преобразования в 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; 
} 

Это дает мне следующее сообщение об ошибке: "неоднозначного преобразования из производного класса„D“для базового класса. "
Но я не вижу, где двусмысленность: если объект, объявленный в main (d), имеет тип D, почему он не может быть напрямую преобразован в тип A?

Кроме того, если я передать аргумент типа строки я, конечно, получить другие ошибки:
'std::basic_string<char>' is not polymorphic

В Java для дженериков есть синтаксис: <T extends A>; в этом случае это было бы полезно. Как я могу сделать аналогичную вещь в C++ с шаблонами?


Я изменил код таким образом:

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

Но я все еще получаю ошибку: 'std::basic_string<char>' is not polymorphic

+0

Я предполагаю, что это всего лишь иллюстративный пример, а не настоящий код? Разумеется, это именно то, что решено полиморфизмом. –

+0

Это не иллюстративно, но я попробовал этот код, чтобы посмотреть, как работает dynamic_cast. –

+1

@RamyAlZuhouri См. [MSDN] (http://msdn.microsoft.com/en-us/library/4k5yex0s%28v=vs.71%29.aspx) об этом. «Вы не можете использовать dynamic_cast для преобразования из неполиморфного класса (класса без виртуальных функций)». – MPelletier

ответ

1

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

Sigh ... использование RTTI.

#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

Это называется deadly diamond of death, или просто, проблема алмазов. «Путь» к A может проходить через B или C, следовательно, потенциальное противоречие.

Кроме того, идея шаблона заключается в том, чтобы сделать его общим, а не знаком с типом. Шаблон не сам по себе скомпилированный код, он скомпилирован против его использования. Это очень похоже на большой макрос.

+1

Виртуальное наследование решает это. Google, если вам интересно. Вот статья Википедии об этом: http://en.wikipedia.org/wiki/Virtual_inheritance –

+1

@ trinithis: сказать, что виртуальное наследование «решает это» вводит в заблуждение - дизайн, как показано, имеет два объекта A и может быть в них нуждается. изменение, чтобы иметь общий объект A, может быть или не быть жизнеспособным. –

+0

Это не бриллиант, потому что есть только два класса класса. –

6

Прежде всего, это не проблема с шаблонами. Если вы удалите шаблон и просто получите print_type, возьмите D*, вы увидите, что ошибка все равно будет там.

Что происходит, вы сделать не использовать виртуальное наследование, следовательно, вы получите такую ​​ситуацию:

A A 
| | 
B C 
\/
    D 

dynamic_cast не знает , которыйA вы имеете в виду.

Для достижения этой цели: (и я полагаю, это то, что вы хотите)

A 
/\ 
B C 
\/
    D 

... вы должны использовать virtual inheritance, Ergo:

class A 
{ 
}; 

class B : virtual public A 
{ 
}; 

class C : virtual public A 
{ 
}; 

class D : public C,public B 
{ 
}; 

... и теперь она составляет без проблем :) (имейте в виду, что Virtual Inheritance Is Evil)

1

Ошибка на самом деле объясняет это довольно хорошо. «Неоднозначное преобразование». Проблема заключается не в том, что он не может отличить D* до A*. Проблема в том, что D* может быть отлит любым из двух разных A* s.Хороший способ увидеть, что проблема должна поместить простое значение в А, как:

class A 
{ 
    public: 
    int value; 
} 

Теперь в B «конструктор s, установите A.value 1, а в C» s конструктора, установите A.value в 2. D теперь имеет один A со значением 1 и другой A со значением 2. Это заставляет задаться вопросом: когда вы хотите направить свой D* на номер A*, какой из них вы хотите?

Смежные вопросы