2014-09-25 23 views
9

Sono confuso sul motivo per cui il mio codice non produce l'errore invalid use of incomplete type, mentre tutta la lettura che ho fatto su questo errore suggerisce che dovrebbe.
La domanda derivava da questo errore mostrando (come previsto) nella parte del mio codice con una struttura simile, ma non riesco a riprodurla in un piccolo esempio (vedere diniego alla fine del domanda).Uso non valido del tipo incompleto: perché non si verifica alcun errore in questo caso?

Sintesi di quello che sto cercando di fare:

  • Ho una struttura (Tree), e voglio assegnare diversi oggetti di tipo di base First ad esso.
  • implementazioni concrete di First hanno valori di ritorno diverse, quindi due livelli di riferimento indiretto vengono utilizzati:
  • First è una classe base astratta, e First * vengono utilizzati per gestire istanze concrete differenti.
  • template <typename Type> class TypedFirst : public First è un tipo astratto che definisce la funzione con tipo di ritorno Type.
  • Infine ConcreteFirstX sono specializzazioni concrete di TypedFirst<Type>.

In tree.tpp, perché la chiamata a new TF(this)non produce l'errore invalid use of incomplete type? (Il punto è segnato nel codice) Penso che l'errore dovrebbe essere lì perché, mentre TF è un modello, quando utilizza ConcreteFirstA, il tree.tpp non è a conoscenza di esso (non include concretefirsta.h o addirittura first.h, è solo forward dichiara First)

Il codice completo, compilabile e eseguibile per questo esempio può essere trovato here on pastebin. Qui, escluderò le protezioni #define e simili, per brevità. Il codice è il seguente:

// tree.h 
class First; 
class Tree{ 
    public: 
    Tree() {} 
    ~Tree() {} 
    template<class TF> // where TF is a ConcreteFirst 
    void addFirstToTree(); 
    private: 
    std::map<std::string, First *> firstCollection; // <- "First"'s here 
}; 
#include "tree.tpp" 

// tree.tpp 
#include "tree.h" 

template <class TF> // where TF is a ConcreteFirst 
void Tree::addFirstToTree(){ 
    this->firstCollection[TF::name] = new TF(this); // <--- Why does this work? 
    //        ^^^^^^^^^^^^^  
} 

// first.h 
class Tree; 
class First{ 
    public: 
    static const std::string name; 
    First(const Tree *baseTree) : myTree(baseTree) {} 
    virtual ~First(); 
    protected: 
    const Tree *myTree; 
}; 

template <typename Type> class TypedFirst : public First{ 
    public: 
    static const std::string name; 
    TypedFirst(const Tree *baseTree) : First(baseTree) {} 
    Type &value() {return this->_value;} 
    private: 
    Type _value; 
}; 
#include "first.tpp" 

// first.tpp 
#include "first.h" 
template <typename Type> 
const std::string TypedFirst<Type>::name = "default typed"; 

// first.cpp 
#include "first.h" 
First::~First() {} 
const std::string First::name = "default"; 

// concretefirsta.h 
#include "first.h" 
class ConcreteFirstA : public TypedFirst<int>{ 
    public: 
    static const std::string name; 
    ConcreteFirstA(const Tree *baseTree) : TypedFirst<int>(baseTree) {} 
    ~ConcreteFirstA() {} 
}; 

// concretefirsta.cpp 
#include "concretefirsta.h" 
const std::string ConcreteFirstA::name = "firstA"; 

Infine, il codice che mette tutto insieme e rende il (a) funzione appropriata chiamate:

// main.cpp 
#include "tree.h" 
#include "first.h" 
#include "concretefirsta.h" 

int main(){ 
    Tree *myTree = new Tree(); 
    myTree->addFirstToTree<ConcreteFirstA>(); // <-- here! why is this working? 
    delete myTree; 
    return 0; 
} 

DISCLAIMER Questa domanda è stata effettivamente motivata da un problema più grande che ho avuto, che ho ritenuto troppo grande e senza risposta in formato Stack Overflow. Anche se inizialmente ho provato a chiedermelo, la domanda è stata chiusa in quanto troppo ampia e ora sto cercando di recuperarla chiedendo solo una parte della domanda.

Il mio problema è che Continuo a ricevere l'errore in un pezzo di codice con struttura identica a questo: ma, non riesco a riprodurlo in un piccolo esempio.

Così, mi chiedo il motivo per cui il seguente pezzo di codice è non produrre l'erroreinvalid use of incomplete type (come ci si aspetta), e spero che mi aiuterà a capire e risolvere il mio problema reale.

Per favore, non dirmi che questo è un caso di the XY problem: so che non sto chiedendo del mio problema reale, perché io (e la comunità) lo ritenevo troppo grande per questo formato.

+0

Hai provato a "sottrarre" il codice finché l'errore di compilazione non scompare? L'altra tecnica consiste nel preprocessare il file src problematico e avviare lo stub di ampie porzioni di codice fino a quando non viene compilato senza quell'errore. Suppongo che tu stia usando una specie di controllo di versione per un facile rollback. – greatwolf

risposta

1

Poiché i modelli non vengono compilati fino a quando non li instanziate con argomenti concreti.

Quando il compilatore arriva alla linea:

myTree->addFirstToTree<ConcreteFirstA>(); 

compila la funzione addFirstToTree per la prima volta con l'argomento ConcreteFirstA, che è un tipo completamente conosciuto lì.

Vedi cplusplus.com - Templates

Essi sono compilati su richiesta, il che significa che il codice di una funzione template non è compilato fino a quando è necessaria un'istanza con argomenti modello specifico. In quel momento, quando è richiesta un'istanza, il compilatore genera una funzione specifica per quegli argomenti dal modello.

2

main.cpp include tree.h, che contiene (indirettamente) la problematica addFirstToTree() modello e concretefirsta.h, che contiene la definizione di ConcreteFirstA.

Più tardi, nel main.cpp, addFirstToTree() viene creata un'istanza per il tipo di ConcreteFirstA:

myTree->addFirstToTree<ConcreteFirstA>(); // <-- here! why is this working? 

Questo è il punto in cui il compilatore ha bisogno di sapere abbastanza circa i tipi usati come parametri di modello per essere in grado di compilatore addFirstToTree() abbastanza. E ne sa abbastanza. ConcreteFirstA è un tipo completo qui, poiché concretefirsta.h è stato incluso e contiene la definizione della classe.


In precedenza in tree.tpp dove addFirstToTree() ottiene definito, ConcreteFirstA non è ancora definita, ma questo non importa. Il compilatore vede un modello e non sa per quali parametri del modello verrà istanziato in seguito. Qualsiasi funzione/... che dipende da un parametro template ("nomi dipendenti") non può essere risolta senza sapere per quali parametri il modello verrà istanziato, in modo che la ricerca dei nomi/... sia rimandata a più tardi.

Una volta che il modello viene istanziato e il compilatore risolve tutti i nomi dipendenti e compila il modello per i parametri specifici del modello. Dal momento che ConcreteFirstA non è un tipo incompleto, a quel punto funziona senza errori.

+0

Ehi! Scusa per la risposta tardiva, mi sono ammalato e solo ora ho avuto il tempo di esaminare la risposta. TY per la risposta.Quindi, fondamentalmente, giusto per chiarire, l'altro punto marcato nel mio codice 'new TF (this)' (in 'tree.tpp') funziona nonostante' tree.tpp' _non_ includendo 'concretefirsta.h' perché in solo viene compilato dopo tutto viene incluso in 'main.cpp'? – penelope

Problemi correlati