2011-08-25 13 views
7

Tengo el siguiente código que no se compila.Plantillas C++ con objetos de interfaz

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

class Derived : public Base { }; 

class NotDerived { }; 

template <typename T> 
class Group { }; 

int main() { 
    Group<Base> *g = NULL; 

    g = new Group<Base>();  // Works 
    g = new Group<Derived>(); // Error, but I want it to work 

    g = new Group<NotDerived>(); // Error, as expected 
} 

entiendo que esto no va a compilar porque g es un tipo diferente de Group<Derived>. Para que esto funcione en Java, haría algo como Group<? extends Base> g, pero C++ no tiene esa palabra clave hasta donde yo sé. ¿Qué se puede hacer?

Editar: Me gustaría aclarar que no quiero que sea posible establecer tipos no derivados de Base como g. He actualizado mi ejemplo para explicar esto.

Editar 2: Hay dos soluciones a mi problema. Dave's Me pareció simple y fácil de definir. Pero Bowie's (junto con Mark's adiciones) satisfizo mis necesidades mejor.

+0

No sé cómo funciona este tipo de cosas en Java. ¿Qué estás tratando de decir con esa sintaxis? –

+0

Esto tampoco funcionaría en Java. La sintaxis de ' 'no es para apoyar la covarianza, sino que está allí para agregar una restricción sobre qué tipos puede usar el genérico. –

+0

Ver [polimorfismo de plantilla C++] (http://stackoverflow.com/questions/2203388/c-templates-polymorphism) – Praetorian

Respuesta

3

usted podría hacer Grupo < Base> una clase base de todos Grupo < T> T! = Base.

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

class Derived : public Base { }; 


template <typename T> class Group; 

struct Empty { }; 

template <typename T> 
struct base_for_group_t { 
    typedef Group<Base> type; 
}; 

template <> 
struct base_for_group_t<Base> { 
    typedef Empty type; 
}; 

template <typename T> 
class Group : public base_for_group_t<T>::type { }; 

int main() { 
    Group<Base> *g = 0; 

    g = new Group<Base>(); // Works 
    g = new Group<Derived>(); // now works 
} 
+0

Desafortunadamente, ahora algo como esto es legal: 'class NotDerived {}; g = nuevo Grupo (); '. Me gustaría mantener el tipo de seguridad si es posible. – Ryan

1

No creo que C++ apoye eso. La plantilla C++ se maneja completamente en tiempo de compilación, por lo que no es compatible con polimorfismo. Eso significa que el tipo de argumentos de la plantilla debe ser exactamente el mismo para ambos lados de la expresión de asignación.

5

Las clases Group<Base> y Group<Derived> son clases sin relación alguna, diferentes. Los indicadores para ellos no son convertibles en ninguna dirección.

Si necesitas comportamiento de ejecución polimórfica, su plantilla de clase Group podría derivar de un (no moldeado) clase base común:

class Group // base 
{ 
    virtual ~Group() { } 
}; 

template <typename T> 
class ConcreteGroup : public Group 
{ 
    // ... 
    T * m_impl; 
}; 

Group * g1 = new ConcreteGroup<A>; 
Group * g1 = new ConcreteGroup<B>; 
+0

ConcreteGroup no necesita heredar Group? –

+0

@Mu: ¡Sí, gracias, corregido! –

2

Bowie Owens's Answer maneja la covarianza que necesita para resolver la pregunta original. En cuanto a la restricción que solicitó en la pregunta editada, puede lograrlo mediante el uso de rasgos de tipo.

template <typename T, class Enable = void> class Group; 

template <typename T> 
class Group<T, typename enable_if<is_base_of<Base, T>::value>::type> 
    : public base_for_group_t<T>::type { }; 
+0

Desafortunadamente, no puedo hacer que compile en mi extremo (no estoy familiarizado con cómo usar correctamente los rasgos de tipo). ¿Puedes dar un ejemplo más completo? (Asumí que 'Group ' se supone que es 'template class Group', sin embargo eso no funcionó. – Ryan

+0

Inténtalo de nuevo con' 'value 'on is_base_of. (Se requiere diferencia entre' boost :: enable_if' y 'std :: enable_if'). Si no, ¿cuál es el error que obtienes? –

+0

Ah, no incluí el espacio de nombres estándar. Tu código original funciona como se esperaba. ;) – Ryan

0

Creo que entiendo lo que está buscando. No estoy seguro de que sea el mejor enfoque (y es posible que desee consultar Boost.Factory).

template <class T> 
class Factory { 

public: 
    virtual T* operator()() = 0; 
}; 

template <class Derived, class Base> 
class ConcreteFactory : public Factory<Base> { 

public: 
    virtual Base* operator()() { 
    return new Derived(); 
    } 
}; 

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

class Derived1 : public Base { }; 
class Derived2: public Base {}; 

class NotDerived {}; 

int main() 
{ 
    Factory<Base>* g; 

    g = new ConcreteFactory<Derived1, Base>; 
    g = new ConcreteFactory<Derived2, Base>; 
    // g = new ConcreteFactory<NotDerived, Base>; // Will not work if you try to do this 
} 
+0

Esta parece ser la solución más segura hasta el momento. La definición de Derived1 y Base es un poco redundante, pero no es algo que ya sepa (y puedo evitarlo con un typedef). Gracias, :) – Ryan

Cuestiones relacionadas