2011-02-10 13 views
11

Zgodnie z poniższym kodem kompilator wyświetla komunikat wskazujący, że error: templates may not be ‘virtual’. Czy ktoś ma sugestię, jak rozwiązać problem?szablony mogą nie być "wirtualne"

template < class FOO_TYPE> 
class CFoo{ 
    public: 
     ... 
     template < class BAR_TYPE > 
     virtual void doSomething(const CBar<BAR_TYPE> &); // here's the error 
     ... 
     virtual ~CFoo(); 
    protected: 
     MyClass <FOO_TYPE> * m_pClass; 
}; 

template < class FOO_TYPE > 
template < class BAR_TYPE > 
void CFoo<FOO_TYPE>::doSomething(const CBar<BAR_TYPE> & refBar){ 
    ... 
} 
+5

To nie jest błąd, to funkcja, nie można zadeklarować szablonu funkcji, który jest wirtualny. Musisz zbadać inne podejście, a to zależy od tego, co próbujesz zrobić ... – Nim

+0

Rzecz w tym, że parametr funkcji jest obiektem szablonu i nie mogę zmienić jego deklaracji. – Javier

+0

dlaczego potrzebujesz innej funkcji dla każdego wystąpienia CBar? –

Odpowiedz

16

Najprostszym powodem, dlaczego jest to nielegalne jest rozważając vtable. Oczywiście, to tylko jedna powszechna implementacja, a inne są dozwolone. Ale wszystkie funkcje w C++ są zaprojektowane tak, że mogą być implementowane za pomocą vtable.

Teraz, ile wpisów jest w vtable z CFoo<int>? Czy istnieje wpis dotyczący doSomething<float>? I doSomething<float*>? I doSomething<float**>? Szablony takie jak te pozwalają na generowanie nieskończonego zestawu funkcji. Zwykle nie stanowi to problemu, ponieważ używasz tylko skończonego podzbioru, ale dla funkcji wirtualnych ten podzbiór nie jest znany, dlatego też vtable musiałby być nieskończony.

Możliwe, że naprawdę chciałeś tylko jednego wpisu w vtable. W takim przypadku, można by napisać to w następujący sposób:

template < class FOO_TYPE, class BAR_TYPE> 
class CFoo{ 
    public: 
     ... 
     virtual void doSomething(const CBar<BAR_TYPE> &); // now OK. 
     ... 
     virtual ~CFoo(); 
    protected: 
     MyClass <FOO_TYPE> * m_pClass; 
}; 

Oznacza to, że vtable dla CFoo<int, float> będzie miał jeden wpis, na doSomething(float const&).

+1

Może to być znane na łączu po skompilowaniu wszystkich jednostek kompilacji. Norma jest trochę szybka odrzucić tutaj. –

+3

@ v.oddou: Nierealistycznie. Łącznik musiałby dopasowywać wszystkie wywołania wirtualne do wszystkich możliwych klas bazowych i tworzyć szablony instancji. Te wystąpienia muszą następnie zostać skompilowane. Te nowe instancje z kolei mogą zawierać nowe wywołania wirtualne, więc proces ten musiałby być powtarzany. – MSalters

1

Cóż, komunikat o błędzie jest całkiem jasny. Member function templates can't be virtual. Sposób rozwiązania tego problemu zależy od twojego problemu, ale najprościej byłoby uczynić funkcje użytkownika nie-wirtualnymi i ponownie rozważyć twój projekt.

+0

dzięki. W moim przypadku potrzebuję tego parametru "refBar" jako parametru i należy on do klasy szablonowej. – Javier

+1

Czy wiesz, ile będzie różnych parametrów szablonu? 3? 8? Czy możesz przeciążyć funkcję każdego z nich? Jeśli nie wiesz, w jaki sposób kompilator będzie wiedział, ile jest funkcji wirtualnych? –

+0

@Bo: tylko 2 parametry: FOO_TYPE i BAR_TYPE – Javier

1

Jeśli naprawdę chcesz, aby ta metoda była wirtualna, rozważ polimorficzne polimorficzne przekształcenie i przekazanie typu podstawowego, który nie jest szablonem.

EDIT: coś takiego:

// non-templated base class 
class BarBase 
{ 
// common methods go here.. 
}; 

template <typename BAR_TYPE> 
class CBar : public BarBase 
{ 
// implement methods from BarBase ... 
}; 

template < class FOO_TYPE> 
class CFoo{ 
    public: 
     ... 
     // now we take the base type, and this method does not need to be a template 
     virtual void doSomething(BarBase const* ptrBar); 
     ... 
     virtual ~CFoo(); 
    protected: 
     MyClass <FOO_TYPE> * m_pClass; 
}; 

template < class FOO_TYPE > 
void CFoo<FOO_TYPE>::doSomething(BarBase const* ptrBar){ 
.. 
} 
+0

Przepraszam, nie dostałem tego. Czy robisz przykład? – Javier

1

Można wykorzystać to, co nazywamy w Symbiana jako „wzorzec projektowania szablonu”. Oto przykładowy kod, aby dać Ci pomysł:

class Base { 
public: 
     virtual int DoSomething() = 0; 
protected: 
     Base(); 
}; 

class IntermediateBase : public Base { 
protected: 
     IntermediateBase(void* aSomeParam, void* aArg) 
     : iSomeParam(aSomeParam) 
     , iArgs(aArg) 
     {} 

     virtual int DoSomething() = 0; 
protected: 
     void* iSomeParam; 
     void* iArgs; 
}; 

template <class TYPE, class INPUT> 
class ConcreteClass : public IntermediateBase { 
     typedef int (TYPE::*MemberFuncPtr)(const INPUT&); 
public: 
     ConcreteClass(TYPE& aCommandType, 
         INPUT& aArgumentsToCommand, 
         MemberFuncPtr aMFP) 
     : IntermediateBase(static_cast<TYPE*>(&aCommandType), 
          static_cast<INPUT*>(&aArgumentsToCommand)) 
     , iMFP(aMFP) 
     {} 

     virtual int DoSomething() // VIRTUAL AND INLINE Note - dont make it 
            // virtual and inline in production if 
            // possible to avoid out-of-line copy 
     { 
      return static_cast<TYPE*>(iSomeParam)->*ConcreteClass::iMFP) 
          (*(static_cast<INPUT*>(iArgs)); 
     } 
private: 
     MemberFuncPtr iMFP; 
}; 
+15

Formatowanie sprawia, że ​​chcę coś zranić. – GManNickG

+0

Przepraszamy, sformatowałem go teraz. – Viren

+0

dzięki za ilustrację kodu. Zdefiniowałem metodę 'doSomething' jako' szablon' z powodu jej argumentu. Istnieją tylko dwa typy szablonów, jeden odpowiadający "CFoo", a drugi z powodu "CBar". Myślę, że deklarując 'CFoo ' jako klasę podwójnego szablonu powinno być OK. Co myślisz? – Javier