2014-09-25 4 views
9

Ich bin verwirrt darüber, warum mein Code nicht den Fehler produziert, während alle lesen, die ich über diesen Fehler getan habe, schlägt vor, es sollte.
Die Frage nach diesem Fehler resultierte zeigt sich mit einer ähnlichen Struktur in dem Teil meines Codes (wie erwartet), aber ich kann es nicht in einem kleinen Beispiel reproduzieren (bitte Haftungsausschluss am Ende der sehen Frage).Ungültige Verwendung von unvollständigem Typ - warum kein Fehler in diesem Fall?

Zusammenfassung von dem, was ich zu tun versucht:

  • Ich habe eine Struktur (Tree), und ich möchte verschiedene Objekte von Basistyp First ihm zuweisen.
  • Konkrete Implementierungen von First haben unterschiedliche Rückgabewerte, so werden zwei Dereferenzierungsebenen verwendet:
  • First ist eine abstrakte Basisklasse, und First * werden verwendet, um verschiedene konkrete Fälle zu behandeln.
  • template <typename Type> class TypedFirst : public First ist ein abstrakter Typ, der die Funktion mit dem Rückgabetyp Type definiert.
  • Schließlich ConcreteFirstX sind konkrete Spezialisierungen von TypedFirst<Type>.

In tree.tpp, warum der Anruf an new TF(this)nicht produzieren die invalid use of incomplete type Fehler entdeckt? (Die Stelle im Code markiert) Ich denke, der Fehler sollte es sein, weil, während TF eine Vorlage, wenn ich ConcreteFirstA verwenden, die tree.tpp nicht bekannt ist (es ist nicht concretefirsta.h nicht enthalten oder sogar first.h, es nur vorwärts erklärt First)

Der vollständige, kompilierbare und ausführbare Code für dieses Beispiel kann here on pastebin gefunden werden. Hier werde ich aus Gründen der Kürze #define Wachen und ähnliche Dinge ausschließen. Der Code ist wie folgt:

// 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"; 

Schließlich ist der Code, dass alle zusammen bringt und macht die (in) entsprechende Funktion aufruft:

// 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 Diese Frage wurde tatsächlich von einem größeren Problem ausgelöst, das ich im Stack Overflow-Format für zu groß und unzuverlässig hielt. Obwohl ich ursprünglich versucht habe, danach zu fragen, wurde die Frage als zu weit geschlossen und ich versuche nun, sie zu retten, indem ich nur einen Teil der Frage stelle.

Mein Problem ist, dass Ich bekomme den Fehler in einem Stück Code mit identischer Struktur zu diesem: aber ich kann es nicht in einem kleinen Beispiel reproduzieren.

So frage ich warum der Code das folgende Stück nicht Herstellung die Fehlerinvalid use of incomplete type (wie ich erwarten würde), und ich hoffe, dass wird mir helfen, mein eigentliches Problem zu verstehen und lösen.

Bitte sagen Sie mir nicht, dass dies ein Fall von the XY problem ist: Ich weiß, dass ich nicht nach meinem tatsächlichen Problem frage, weil ich (und die Gemeinschaft) es für dieses Format für zu groß hielt.

+0

Haben Sie "subtraktiv" versucht, den Code auszublenden, bis der Kompilierungsfehler verschwindet? Die andere Technik besteht darin, die problematische src-Datei vorzufunktionieren und einen großen Codeabschnitt auszugeben, bis er ohne diesen Fehler kompiliert wird. Ich nehme an, dass Sie eine Art Versionskontrolle für das einfache Zurücksetzen verwenden. – greatwolf

Antwort

1

Da Vorlagen nicht kompiliert werden, bis Sie sie mit konkreten Argumenten instanziieren.

Wenn der Compiler kommt mit der Leitung:

myTree->addFirstToTree<ConcreteFirstA>(); 

es kompiliert die Funktion addFirstToTree zum ersten Mal mit dem Argumente ConcreteFirstA, die ein vollständig bekannter Typ ist.

Siehe cplusplus.com - Templates

Sie werden bei Bedarf kompiliert, was bedeutet, dass der Code einer Template-Funktion nicht, bis eine Instantiierung mit bestimmten Vorlage Argumente zusammengestellt erforderlich ist. In diesem Moment, wenn eine Instantiierung erforderlich ist, generiert der Compiler eine Funktion speziell für diese Argumente aus der Vorlage.

2

main.cpp umfasst tree.h, der (indirekt) enthält die problematische addFirstToTree() Templat und concretefirsta.h, die die Definition von ConcreteFirstA enthält.

Später in main.cpp wird addFirstToTree() für die ConcreteFirstA Typ instanziiert:

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

Dies ist der Punkt, wo der Compiler genug genug über die Typen als Template-Parameter verwendet wissen muss in der Lage sein addFirstToTree() Compiler. Und es weiß genug. ConcreteFirstA ist hier ein vollständiger Typ, da concretefirsta.h enthalten ist und die Klassendefinition enthält.


auf Früher tree.tpp wo addFirstToTree() definiert wird, wird ConcreteFirstA noch nicht definiert, aber das spielt keine Rolle. Der Compiler sieht eine Vorlage und weiß nicht, für welche Template-Parameter diese Vorlage später instanziiert wird. Alle Funktionen/..., die von einem Template-Parameter ("abhängige Namen") abhängen, können nicht aufgelöst werden, ohne zu wissen, für welche Parameter das Template instanziiert wird, so dass name lookup/... erst später verschoben wird.

Sobald die Vorlage instanziiert ist und der Compiler alle abhängigen Namen auflöst und die Vorlage für die spezifischen Vorlagenparameter kompiliert. Da ConcreteFirstA zu diesem Zeitpunkt kein unvollständiger Typ ist, funktioniert dies ohne Fehler.

+0

Hey! Entschuldigung für die späte Antwort, ich bin krank geworden und hatte erst jetzt Zeit, über die Antwort nachzudenken. TY für die Antwort.Also im Grunde, um nur zu verdeutlichen, funktioniert der andere markierte Punkt in meinem Code 'new TF (this)' (in 'tree.tpp') trotz' tree.tpp' _not_ inclusive 'concretefirsta.h', denn in wird nur nach kompiliert alles wird in 'main.cpp' eingeschlossen? – penelope

Verwandte Themen