2009-03-16 17 views
50

Sto provando a utilizzare un typedef da una sottoclasse nel mio progetto, ho isolato il mio problema nell'esempio seguente.uso non valido di tipo incompleto

Qualcuno sa dove sto andando male?

template<typename Subclass> 
class A { 
    public: 
     //Why doesn't it like this? 
     void action(typename Subclass::mytype var) { 
      (static_cast<Subclass*>(this))->do_action(var); 
     } 
}; 

class B : public A<B> { 
    public: 
     typedef int mytype; 

     B() {} 

     void do_action(mytype var) { 
      // Do stuff 
     } 
}; 

int main(int argc, char** argv) { 
    B myInstance; 
    return 0; 
} 

Questa è l'uscita ottengo:

[email protected]:~/Documents/LucadeStudios/experiments$ g++ -o test test.cpp 
test.cpp: In instantiation of ‘A<B>’: 
test.cpp:10: instantiated from here 
test.cpp:5: error: invalid use of incomplete type ‘class B’ 
test.cpp:10: error: forward declaration of ‘class B’ 

risposta

57

La ragione è che, quando un'istanza di un modello di classe, tutte le sue dichiarazioni (non le definizioni) delle sue funzioni membro sono anche istanziate. Il modello di classe viene istanziato precisamente quando è richiesta la definizione completa di una specializzazione. Questo è il caso in cui viene utilizzato come classe base, ad esempio, come nel tuo caso.

Quindi ciò che accade è che A<B> viene creata un'istanza in

class B : public A<B> 

a quel punto non B un tipo completo è ancora (è dopo la parentesi di definizione della classe di chiusura). Tuttavia, la dichiarazione A<B>::action s' impone B per essere completa, perché sta strisciando nel campo di applicazione di esso:

Subclass::mytype 

Quello che dovete fare è ritardare l'istanza a un certo punto in cui B è completa. Un modo per farlo è modificare la dichiarazione di action per renderlo un modello membro.

template<typename T> 
void action(T var) { 
    (static_cast<Subclass*>(this))->do_action(var); 
} 

E 'ancora il tipo di sicurezza, perché se var non è del tipo giusto, passando var-do_action fallirà.

+1

Ho optato per una leggera ristrutturazione del mio codice (a causa di altri problemi correlati che non ho descritto qui) ma ho provato questo approccio e risolve il problema. Grazie! – seanhodges

0

È necessario utilizzare un puntatore o un riferimento come il tipo corretto non è noto in questo momento il compilatore non può un'istanza.

cercare invece:

void action(const typename Subclass::mytype &var) { 
      (static_cast<Subclass*>(this))->do_action(); 
    } 
+0

Ho provato a cambiarlo su un riferimento e quindi su un puntatore e l'errore è sempre lo stesso. Capisco il tuo punto però. – seanhodges

2

si deriva da BA<B>, quindi la prima cosa che fa il compilatore, una volta che si vede la definizione di classe B è quello di cercare di istanziare A<B>. Per farlo, è necessario conoscere B::mytype per il parametro action. Ma dal momento che il compilatore è solo in procinto di capire la definizione attuale di B, non conosce ancora questo tipo e si ottiene un errore.

Un modo per aggirare questo è sarebbe quello di dichiarare il tipo di parametro come un altro parametro di template, invece che all'interno della classe derivata:

template<typename Subclass, typename Param> 
class A { 
    public: 
     void action(Param var) { 
       (static_cast<Subclass*>(this))->do_action(var); 
     } 
}; 

class B : public A<B, int> { ... }; 
1

Non è esattamente quello che stavi chiedendo, ma si può fare l'azione di una funzione di membro template:

template<typename Subclass> 
class A { 
    public: 
     //Why doesn't it like this? 
     template<class V> void action(V var) { 
       (static_cast<Subclass*>(this))->do_action(); 
     } 
}; 

class B : public A<B> { 
    public: 
     typedef int mytype; 

     B() {} 

     void do_action(mytype var) { 
       // Do stuff 
     } 
}; 

int main(int argc, char** argv) { 
    B myInstance; 
    return 0; 
} 
22

Si può ovviare a questo utilizzando una classe tratti:
Si richiede di impostare una classe tratti specializzate, per ogni classe di actuall che usi.

template<typename SubClass> 
class SubClass_traits 
{}; 

template<typename Subclass> 
class A { 
    public: 
     void action(typename SubClass_traits<Subclass>::mytype var) 
     { 
       (static_cast<Subclass*>(this))->do_action(var); 
     } 
}; 


// Definitions for B 
class B; // Forward declare 

template<> // Define traits for B. So other classes can use it. 
class SubClass_traits<B> 
{ 
    public: 
     typedef int mytype; 
}; 

// Define B 
class B : public A<B> 
{ 
    // Define mytype in terms of the traits type. 
    typedef SubClass_traits<B>::mytype mytype; 
    public: 

     B() {} 

     void do_action(mytype var) { 
       // Do stuff 
     } 
}; 

int main(int argc, char** argv) 
{ 
    B myInstance; 
    return 0; 
} 
Problemi correlati