2011-02-10 9 views
11

Étant donné le code ci-dessous, le compilateur affiche un message indiquant error: templates may not be ‘virtual’. Quelqu'un at-il une suggestion sur la façon de résoudre le bug?Les modèles ne peuvent pas être 'virtuels'

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

Ce n'est pas un bug, c'est une fonctionnalité, vous ne pouvez pas déclarer un modèle de fonction qui est virtuel. Vous devez explorer une autre approche, et cela dépend de ce que vous essayez de faire ... – Nim

+0

le fait est que le paramètre de la fonction est un objet modèle et je ne peux pas changer sa déclaration. – Javier

+0

Pourquoi avez-vous besoin d'une fonction différente pour chaque instance de CBar? –

Répondre

16

La raison la plus simple de voir pourquoi cela est illégal est de considérer le vtable. Bien sûr, ce n'est qu'une implémentation commune, et d'autres sont autorisées. Mais toutes les fonctions virtual en C++ sont conçues de telle sorte qu'elles peuvent être implémentées avec un vtable.

Maintenant, combien y a-t-il d'entrées dans le vtable de CFoo<int>? Y at-il une entrée pour doSomething<float>? Et doSomething<float*>? Et doSomething<float**>? Des modèles tels que ceux-ci permettent de générer un ensemble infini de fonctions. Habituellement, cela ne pose aucun problème, car vous n'utilisez qu'un sous-ensemble fini, mais pour les fonctions virtuelles, ce sous-ensemble n'est pas connu et, par conséquent, le vtable doit être infini.

Maintenant, il est possible que vous vouliez vraiment qu'une seule entrée dans le vtable. Dans ce cas, vous écrivez comme suit:

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

Cela signifie que le vtable pour CFoo<int, float> aura une entrée, pour doSomething(float const&).

+1

Il pourrait être connu à l'étape de liaison après que toutes les unités de compilation ont été compilées. La norme est un peu rapide sur la défausse ici. –

+3

@ v.oddou: Pas de façon réaliste. L'éditeur de liens devrait faire correspondre tous les appels virtuels à toutes les classes de base possibles et instancier des modèles. Ces instanciations doivent ensuite être compilées. Ces nouvelles instanciations peuvent à leur tour contenir de nouveaux appels virtuels, ce processus devrait donc être itératif. – MSalters

1

Eh bien, le message d'erreur est assez clair. Member function templates can't be virtual. Comment résoudre cela dépend de votre problème, mais la chose la plus simple à faire serait de rendre les fonctions membres non virtuelles et de reconsidérer votre conception.

+0

merci. Dans mon cas, j'ai besoin de ce "refBar" comme paramètre et il appartient à une classe modélisée. – Javier

+1

Savez-vous combien de paramètres de modèles différents il y aura? 3? 8? Pouvez-vous surcharger la fonction pour chacun d'eux? Si vous ne savez pas, comment le compilateur connaîtra-t-il le nombre de fonctions virtuelles? –

+0

@Bo: seulement 2 paramètres: FOO_TYPE et BAR_TYPE – Javier

1

Si vous avez vraiment besoin de rendre cette méthode virtuelle, envisagez de créer CBar<> polymorphe et passez un type de base dans lequel il n'y a pas de modèle.

EDIT: quelque chose comme ceci:

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

désolé, je n'ai pas compris. cela vous dérange-t-il de faire un exemple? – Javier

1

Vous pouvez utiliser ce que nous appelons dans Symbian comme "modèle de conception de modèle". Voici un exemple de code pour vous donner une idée:

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

Le formatage me donne envie de blesser quelque chose. – GManNickG

+0

Désolé, je l'ai formaté maintenant. – Viren

+0

merci pour l'illustration du code. J'ai défini la méthode 'doSomething' comme' template' à cause de son argument. Il n'y a que deux types de modèles, l'un correspondant à 'CFoo' et l'autre à' CBar'. Je pense qu'en déclarant 'CFoo ' comme une classe de modèle double devrait être OK. Qu'est-ce que tu penses? – Javier

Questions connexes