2010-02-17 15 views
8

Tengo dos clases que dependen unos de otros:Uso no válido de tipo incompleto en g ++

class Foo; //forward declaration 

template <typename T> 
class Bar { 
    public: 
    Foo* foo_ptr; 
    void DoSomething() { 
     foo_ptr->DoSomething(); 
    } 
}; 

class Foo { 
    public: 
    Bar<Foo>* bar_ptr; 
    void DoSomething() { 
     bar_ptr->DoSomething(); 
    } 
}; 

cuando compilo en g ++, que estaba dando error de "uso no válido de tipo incompleto", pero era compilado muy bien en MSVC 10. ¿Es posible resolver este problema manteniendo la declaración y la definición en un archivo de encabezado? (no hay archivos cpp) Si esto no está permitido en el estándar, ¿este es uno de los "errores" o "características" de MSVC?

+0

Btw compila bien en Linux. Después de agregar puntos y comas al final de las declaraciones de clase, ¿ha intentado mover la implementación a un archivo cpp y dejar solo las declaraciones en el encabezado? Dado que Foo y Bar se hacen referencia, ¿llamar a DoSomething creará un bucle infinito? – stefanB

+0

Uso g ++ 4.4.1, y está dando error: uso no válido del tipo incompleto 'struct Foo'. Por favor, no les moleste el ciclo infinito, les he hecho llamar erróneamente al mismo método, pero podemos considerar que la clase Foo y Bar tienen otros métodos. – leiiv

+2

G ++ tiene razón: 'DoSomething' es un nombre no dependiente, porque no hay nada en el calificador' foo_ptr-> 'que lo haga depender de un parámetro de plantilla. Como tal, tiene que buscarse en el tiempo de definición de la plantilla (pero en ese momento, 'DoSomething' aún no se había declarado). Los nombres dependientes se buscan en el momento de creación de instancias, en su lugar. Como el compilador MSVC++ no implementa esta búsqueda de dos fases, no detecta este error en su programa. –

Respuesta

8

Sí, sólo hay que mover las definiciones de métodos fuera de la definición de clase:

class Foo; //forward declaration 

template <typename T> 
class Bar { 
    public: 
    Foo* foo_ptr; 
    void DoSomething(); 
}; 

class Foo { 
    public: 
    Bar<Foo>* bar_ptr; 
    void DoSomething() { 
     bar_ptr->DoSomething(); 
    } 
}; 

// Don't forget to make the function inline or you'll end up 
// with multiple definitions 
template <typename T> 
inline void Bar<T>::DoSomething() { 
    foo_ptr->DoSomething(); 
} 
+1

Entonces, ¿no hay forma de hacerlo funcionar sin separar la implementación de clase de la definición? – leiiv

+1

@leiiv - No sé de ninguna manera portátil. Pero para ser claros, la implementación aún puede ir en el archivo de encabezado; no se requiere un nuevo archivo fuente –

0

Funciona cuando se reemplaza Foo* foo_ptr; por el parámetro de plantilla T de manera que se obtiene T* foo_ptr;. En este foo_ptr no necesariamente tiene que ser un puntero ni estar predefinido.

template <typename T> 
class Bar { 
    public: 
    T foo; 
    void DoSomething() { 
     foo.DoSomething(); 
    } 
}; 

class Foo { 
    public: 
    Bar<Foo>* bar_ptr; 
    void DoSomething() { 
     bar_ptr->DoSomething(); 
    } 
}; 
Cuestiones relacionadas