2012-03-05 9 views
5

tengo la siguiente jerarquía:downcasting desde un puntero base para los tipos derivados de plantilla

class base 
{ 
public: 
    virtual ~base(){} 
    virtual void foo() {} 
}; 

template <typename T> 
class derived1 : public base 
{ 
    virtual void foo() {}; 
}; 

template <typename T> 
class derived2 : public base 
{ 
    virtual void foo() {}; 
}; 

Ahora da un puntero a la base, me gustaría saber si el subyacente es bien Derived1 o derived2. El problema es que tanto derivado1 como derivado2 pueden especializarse en muchos tipos diferentes de , utilizando dynamic_cast para probar si un lanzamiento descendente requiere que se conozca el tipo de plantilla . He terminado con poco desordenado, unmaintable e incompleta de código:

base* b = new derived1<int>(); 

if (dynamic_cast<derived1<int>*> || 
    dynamic_cast<derived1<unsigned int>*> || 
    dynamic_cast<derived1<double>*>) 
    std::cout << "is derived1"; 
else if (dynamic_cast<derived2<int>*> || 
    dynamic_cast<derived2<unsigned int>*> || 
    dynamic_cast<derived2<double>*>) 
    std::cout << "is derived2"; 

¿Hay una mejor manera, que puede manejar cualquier tipo de especialización?

+2

¿Qué piensas hacer una vez que sabes que el tipo es una especialización de una de las plantillas derivadas? –

+0

@James: llame a una función en particular para cada uno - por cierto, hay más que derivada1 y derivada2 –

+6

El código condicional que necesita probar dinámicamente el tipo concreto de un puntero de clase base es un mal olor de código. Probablemente haya una manera de cambiar su diseño para evitar esto. –

Respuesta

8

Mueva la lógica que depende del tipo al tipo.

En lugar de:

if (dynamic_cast<derived1<int>*>(b) || 
    dynamic_cast<derived1<unsigned int>*>(b) || 
    dynamic_cast<derived1<double>*>(b)) 
    std::cout << "is derived1"; 
else if (dynamic_cast<derived2<int>*>(b) || 
    dynamic_cast<derived2<unsigned int>*>(b) || 
    dynamic_cast<derived2<double>*>(b)) 
    std::cout << "is derived2"; 

agregar una función virtual print_name() const a base, y luego hacer:

void example() { 
    std::unique_ptr<base> b(new derived1<int>()); 
    b->print_name(); 
} 
class base 
{ 
public: 
    ~base(){} 
    virtual void foo() {} 
    virtual void print_name() const = 0; 
}; 

template <typename T> 
class derived1 : public base 
{ 
    virtual void foo() {} 
    virtual void print_name() const { 
     std::cout << "is derived1"; 
    } 
}; 

template <typename T> 
class derived2 : public base 
{ 
    virtual void foo() {} 
    virtual void print_name() const { 
     std::cout << "is derived2"; 
    } 
}; 
6

Inserte una clase no moldeado entre medio base y derived1 o derived2:

class base 
{ 
public: 
    virtual ~base() {} // **NOTE** Should be virtual 
    virtual void foo() {} 
}; 

class derived1_base : public base 
{ 
}; 

template <typename T> 
class derived1 : public derived1_base 
{ 
public: 
    virtual void foo() {} 
}; 

class derived2_base : public base 
{ 
}; 

template <typename T> 
class derived2 : public derived2_base 
{ 
public: 
    virtual void foo() {} 
}; 

En un comentario, usted ha mencionado:

[quiero] llamar a una función particular para cada uno - por cierto que hay más de Derived1 y derived2

añadir que la función (virtual) a derived1_base, y ya no necesita saber T.

if (dynamic_cast<derived1_base*>(foo)) 
{ 
    std::cout << "is derived1"; 
    dynamic_cast<derived1_base*>(foo)->specific_derived1_function(); 
} 
else if (dynamic_cast<derived2_base*>(foo)) 
{ 
    std::cout << "is derived2"; 
    dynamic_cast<derived2_base*>(foo)->specific_derived2_function(); 
} 

NOTA: considero una lista de dynamic_cast<> un código olor, y le insto a reconsiderar su enfoque.

2

Solución 1: agregar una función más virtual:

enum DerivedType 
{ 
    One, 
    Two, 
    ... 
}; 

class base 
{ 
public: 
    ~base(){} 
    virtual void foo() {} 
    virtual DerivedType GetType() = 0; 
}; 

template <typename T> 
class derived1 : public base 
{ 
    virtual void foo() {}; 
    virtual DerivedType GetType() { return One; } 
}; 

template <typename T> 
class derived2 : public base 
{ 
    virtual void foo() {}; 
    virtual DerivedType GetType() { return Two; } 
}; 

Solución 2: uso de clases de etiquetas:

class Base 
{ 
public: 
    virtual ~Base() { } 
}; 

class Derived1Tag 
{ }; 

class Derived2Tag 
{ }; 

template <class T> 
class Derived1 : public Base, public Derived1Tag 
{ }; 

template <class T> 
class Derived2 : public Base, public Derived2Tag 
{ }; 


int main(int argc, char** argv) 
{ 
    Derived1<int> d1; 
    Derived2<int> d2; 

    cout << dynamic_cast<Derived1Tag*>((Base*)&d1) << endl; 
    cout << dynamic_cast<Derived1Tag*>((Base*)&d2) << endl; 

    return 0; 
} 
+0

Debo decir que @EmileCormier tiene razón y que esto ** probablemente ** refleja una mala elección de diseño en alguna parte. Si la función que desea ejecutar depende del tipo, entonces probablemente debería ser una función virtual desde el principio. –

3

Se podría añadir un método virtual para hacer un registro de entrada en meta de algún tipo :

class base 
{ 
public: 
    ~base(){} 
    virtual void foo() {} 
    virtual bool isa(const char* type_to_test){ 
      return strcmp(type_to_test,"base")==0;} 
}; 

template <typename T> 
class derived1 : public base 
{ 
    virtual void foo() {}; 
    virtual bool isa(const char* type_to_test){ 
    return strcmp(type_to_test,"derived1")==0;} 
}; 
Cuestiones relacionadas