2014-09-25 18 views
9

Jestem zdezorientowany tym, dlaczego mój kod nie generuje błędu invalid use of incomplete type, podczas gdy cały odczyt, jaki zrobiłem na temat tego błędu, sugeruje, że powinien.
Pytanie wynika z tego błędu pokazuje się (zgodnie z oczekiwaniami) w ramach mojego kodu o podobnej strukturze, ale nie mogę odtworzyć go w małej przykład (patrz zrzeczenie na koniec pytanie).Nieprawidłowe użycie typu niekompletnego - dlaczego nie ma w tym przypadku błędu?

podsumowanie tego, co próbuję zrobić:

  • Mam strukturę (Tree) i chcę, aby przypisać różne obiekty typu bazowego First do niego.
  • Konkretne implementacje First mają różne wartości zwracanych, więc stosuje się dwa poziomy zadnie:
  • First jest abstrakcyjną klasą bazową, a First * służą do obsługi różnych instancji betonu.
  • template <typename Type> class TypedFirst : public First to typ abstrakcyjny, który definiuje funkcję z typem powrotu Type.
  • Wreszcie ConcreteFirstX są konkretne specjalizacje TypedFirst<Type>.

W tree.tpp, dlaczego wywołanie new TF(this)nie produkować błąd invalid use of incomplete type? (Plamka jest oznaczona w kodzie) Myślę, że błąd nie powinno tam być, ponieważ podczas TF jest szablon, kiedy za pomocą ConcreteFirstA The tree.tpp nie jest tego świadomy (nie obejmuje concretefirsta.h lub nawet first.h, to tylko forward deklaruje First)

Pełny, nadający się do kompilacji i działający kod dla tego przykładu można znaleźć here on pastebin. Tutaj, będę wykluczać strażników i podobne rzeczy, dla zwięzłości. Kod jest w następujący sposób:

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

Wreszcie kod, który przynosi to wszystko razem sprawia, że ​​(w) odpowiednie wywołania funkcji:

// 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 To pytanie było zmotywowane przez większy problem, który miałem, który uważałem za zbyt duży i niemożliwy do odpowiedzi w formacie Stack Overflow. Chociaż początkowo starałem się o to zapytać, pytanie zostało zamknięte jako zbyt szerokie i teraz próbuję je uratować, zadając tylko część pytania.

Mój problem polega na tym, że Ciągle otrzymuję błąd w kawałku kodu o identycznej strukturze do tego: ale nie mogę go odtworzyć w małym przykładzie.

Zatem pytam dlaczego następujący fragment kodu jest nie produkujących błądinvalid use of incomplete type (jak bym się spodziewał) i mam nadzieję, że pomoże mi zrozumieć i rozwiązać mój rzeczywisty problem.

Proszę, nie mów mi, że jest to przypadek the XY problem: Wiem, że nie pytam o mój faktyczny problem, ponieważ ja (i społeczność) uważałem, że jest zbyt duży dla tego formatu.

+0

Czy próbowałeś "odejmowanie" wycinania kodu, dopóki błąd kompilacji nie zniknie? Inną techniką jest wstępne przetworzenie problematycznego pliku src i rozpoczęcie odgałęzienia dużego zakresu kodu, aż kompiluje się bez tego błędu. Zakładam, że używasz jakiejś kontroli wersji do łatwego wycofania. – greatwolf

Odpowiedz

1

Ponieważ szablony nie są kompilowane, dopóki nie zainicjujesz ich konkretnymi argumentami.

Gdy kompilator pochodzi z linii:

myTree->addFirstToTree<ConcreteFirstA>(); 

kompiluje funkcji addFirstToTree po raz pierwszy z argumentów ConcreteFirstA, który jest całkowicie znany typ tam.

Zobacz cplusplus.com - Templates

Są one zestawiane na żądanie, co oznacza, że ​​kod funkcji szablonu nie jest kompilowany aż wymagana jest instancji konkretnych argumentów szablonu. W tym momencie, gdy wymagana jest instancja, kompilator generuje funkcję specjalnie dla tych argumentów z szablonu.

2

main.cpp obejmuje tree.h, który zawiera (pośrednio) problematycznej addFirstToTree() szablonu, a concretefirsta.h, który zawiera definicję ConcreteFirstA.

Później main.cpp, addFirstToTree() jest tworzony dla typu ConcreteFirstA:

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

Jest to punkt, w którym kompilator musi wiedzieć wystarczająco dużo o rodzajach stosowanych jako parametrów szablonu, aby móc kompilator addFirstToTree() wystarczy. I wie wystarczająco dużo. ConcreteFirstA to kompletny typ, ponieważ concretefirsta.h został dołączony i zawiera definicję klasy.


Wcześniej w tree.tpp gdzie addFirstToTree() zostanie zdefiniowany, ConcreteFirstA nie została jeszcze określona, ​​ale to nie ma znaczenia. Kompilator widzi szablon i nie wie, dla jakich parametrów szablonu będzie tworzony później ten szablon. Żadne funkcje/..., które zależą od parametru szablonu ("nazw zależnych") nie mogą zostać rozwiązane bez wiedzy o parametrach, których szablon będzie instancjonowany, tak aby wyszukiwanie nazw/... było odkładane na później.

Gdy szablon zostanie utworzony, a kompilator rozwiąże wszystkie nazwy zależne i skompiluje szablon dla określonych parametrów szablonu. Ponieważ ConcreteFirstA nie jest niekompletnym typem, działa to bezbłędnie.

+0

Hej! Przepraszam za spóźnioną odpowiedź, zachorowałem i dopiero teraz miałem trochę czasu, aby przejrzeć odpowiedź. TY dla odpowiedzi.Tak więc, po prostu dla wyjaśnienia, inne oznaczone miejsce w moim kodzie 'nowe TF (this)' (w 'tree.tpp') działa pomimo' tree.tpp' _not_ w tym 'concretefirsta.h' ponieważ tylko zostaje skompilowane po wszystko jest zawarte w 'main.cpp'? – penelope

Powiązane problemy