2010-07-28 21 views
16

Tengo dos clases, Foo<T> y Bar<T>, derivadas de Base. Cada uno reemplaza un método virtual Base* convert(ID) const, donde ID es una instancia de un tipo que identifica de manera única una instanciación particular de Foo o Bar (pretende ser un enum). El problema es que Foo::convert() necesita para poder devolver una instancia de Bar, y del mismo modo Bar::convert() debe ser capaz de crear una instancia de Foo. Como ambas son plantillas, esto da como resultado una dependencia circular entre Foo.h y Bar.h. ¿Cómo resuelvo esto?Resolviendo una dependencia circular entre clases de plantilla

Editar: Una declaración adelantada no funciona debido a la aplicación de cada método necesita el constructor de la otra clase:

Foo.h:

#include <Base.h> 

template<class T> class Bar; 

template<class T> 
class Foo : public Base { ... }; 

template<class T> 
Base* Foo<T>::convert(ID id) const { 

    if (id == BAR_INT) 
     return new Bar<int>(value); // Error. 

    ... 

} 

Bar.h:

#include <Base.h> 

template<class T> class Foo; 

template<class T> 
class Bar : public Base { ... }; 

template<class T> 
Base* Bar<T>::convert(ID id) const { 

    if (id == FOO_FLOAT) 
     return new Foo<float>(value); // Error. 

    ... 

} 

El error es, naturalmente, "uso no válido del tipo incompleto".

+0

dependencias cíclicas son rara vez es una buena idea. Intenta refactorizar para que la dependencia se rompa. Una primera idea sería mover el método 'convert' a una función libre que depende de' Bar' y 'Foo' ... –

Respuesta

18

Lo que debe hacer es separar las declaraciones de clase de la implementación. Así que algo como

template <class T> class Foo : public Base 
{ 
    public: 
    Base* convert(ID) const; 
} 

template <class T> class Bar : public Base 
{ 
    public: 
    Base* convert(ID) const; 
} 

template <class T> Base* Foo<T>::convert(ID) const {return new Bar<T>;} 
template <class T> Base* Bar<T>::convert(ID) const {return new Foo<T>;} 

De esta manera, tiene definiciones de clase completas cuando se definen las funciones.

+0

Esto suena prometedor. Estoy jugando con eso. –

+0

¡Todo listo! Muchas gracias. –

+0

Me tomó un par de lecturas para resolver lo que está haciendo con esta respuesta: al combinar los dos encabezados y al poner las definiciones de función debajo de ambas declaraciones de clase, evita los problemas, una buena idea. También me ayudó –

9

(Actualizado) Debería poder manejarlo igual que con las clases que no son de plantilla. Escribe tu Bar.h así. (Y lo mismo para foo.h)

#if !defined(BAR_H_INCLUDED) 
#define BAR_H_INCLUDED 

template <class T> 
class Foo; 

template <class T> 
class Bar 
{ 
    /// Declarations, no implementations. 
}  

#include "Foo.h" 

template <class T> 
Base* Bar<T>::Convert() { /* implementation here... */ } 
#endif 
+0

No hay dados. Las clases no pueden ser declaradas hacia adelante porque necesito hacer uso de sus miembros, o al menos del constructor, para poder realizar la conversión. Obtengo el "uso no válido esperado del tipo incompleto". –

+0

@Jon: Ver publicación actualizada. –

+0

La solución que obtuve a partir de la respuesta de KeithB es similar a esto, pero no creo que esto realmente se compile porque 'Foo.h' y' Bar.h' todavía tendrían que incluirse, por lo que uno podría aparecer vacío -entregó. –

11

Debe utilizar declaraciones adelantadas clase de plantilla en cualquiera de las cabeceras

template <class T> 
class X; 

es perfectamente bueno declaración adelantada clase de plantilla.

7

La respuesta de James Curran es un regalo del cielo. En términos generales, la idea de James es restringir la inclusión de archivos de encabezado necesarios hasta el momento en que los miembros ('declaraciones') provenientes de los archivos de encabezado incluidos sean necesarios. A modo de ejemplo:

t1.hh

#ifndef S_SIGNATURE 
#define S_SIGNATURE 

struct G; // forward declaration 

template<typename T> 
struct S { 
    void s_method(G &); 
}; 

#include "t2.hh" // now we only need G's member declarations 

template<typename T> 
void S<T>::s_method(G&g) { g.g_method(*this); } 

#endif 

t2.hh

#ifndef G_SIGNATURE 
#define G_SIGNATURE 

template<typename T> 
struct S; // forward declaration 

struct G { 
    template<typename T> 
    void g_method(S<T>&); 
}; 

#include "t1.hh" // now we only need S' member declarations 

template<typename T> 
void G::g_method(S<T>& s) { s.s_method(*this); } 

#endif 

t.cc

#include "t1.hh" 
#include "t2.hh" 

S<int> s; 
G g; 

int main(int argc,char**argv) { 
    g.g_method(s); // instantiation of G::g_method<int>(S<int>&) 
} 
+0

Solo tuve que votar una respuesta que comienza "La respuesta de James Curran es un regalo del cielo" –

Cuestiones relacionadas