2009-03-16 23 views
50

Próbuję użyć typedef z podklasy w moim projekcie, wyizolowałem mój problem w poniższym przykładzie.nieprawidłowe użycie niekompletnego typu

Czy ktoś wie, gdzie idę źle?

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; 
} 

To wyjście uzyskać:

[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’ 

Odpowiedz

57

Powodem jest to, że przy uruchamianiu szablonu klasy, wszystkie jego deklaracje (nie definicje) jego funkcji składowych są również tworzone. Szablon klasy jest tworzony dokładnie wtedy, gdy wymagana jest pełna definicja specjalizacji. Jest tak w przypadku, gdy jest używany jako klasa bazowa, na przykład, jak w twoim przypadku.

Więc co się dzieje, że A<B> jest tworzony na

class B : public A<B> 

w którym momencie B nie kompletny typ jest jeszcze (to jest po nawiasie zamykającym definicji klasy). Jednak deklaracja A<B>::action „s wymaga B być kompletna, ponieważ jest przeszukiwania w zakresie IT:

Subclass::mytype 

Co trzeba zrobić, to opóźnianie instancji do pewnego punktu, w którym B jest kompletna. Jednym ze sposobów na to jest zmodyfikowanie deklaracji action, aby uczynić ją szablonem członka.

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

Jest jeszcze typ bezpieczny, ponieważ jeśli var nie jest odpowiedni rodzaj, przechodząc var do do_action zawiedzie.

+1

Postanowiłem przeprowadzić niewielką restrukturyzację mojego kodu (z powodu innych powiązanych zagadnień, których tutaj nie opisałem), ale przetestowałem to podejście i rzeczywiście naprawiłem problem. Dzięki! – seanhodges

0

Trzeba użyć wskaźnika lub odwołanie jako właściwego typu nie jest znany w tym czasie kompilator nie może instancję.

Zamiast próbować:

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

Próbowałem zmienić to odniesienie, a następnie wskaźnik, a błąd jest nadal taki sam. Rozumiem jednak twoją sprawę. – seanhodges

2

można czerpać B z A<B>, więc pierwszą rzeczą kompilator robi, gdy widzi definicję klasy B jest próba wystąpienia A<B>. Aby to zrobić, musi znać B::mytype dla parametru action. Ale ponieważ kompilator właśnie opracowuje aktualną definicję B, nie zna jeszcze tego typu i pojawia się błąd.

Jednym ze sposobów na to jest byłoby zadeklarować typ parametru jako inny parametr szablonu, zamiast wewnątrz klasy pochodnej:

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

Niezupełnie co pytałeś, ale można było działanie funkcji składowej Szablon:

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

Można to obejść stosując klasę cech:
Wymaga skonfigurowaniu specialsed klasę cech dla każdej klasy actuall, której używasz.

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; 
} 
Powiązane problemy