Concevoir des classes hiearchical avec une fonction template

J’écris une Base classe qui a une fonction membre prenant un paramètre de modèle:

 class Base { template void func(const T& t) { ... } }; 

Il existe une classe Derived qui hérite conceptuellement de la nature de Base et a la même fonction func avec une implémentation différente.

Au début, j’ai pensé dériver Derived from Base et rendre func virtuel, mais je ne peux pas, car c’est un modèle.

J’ai aussi pensé à CRTP, mais c’est une option car les instances doivent pouvoir être placées dans un conteneur et être accessibles sans connaître les types exacts d’entre elles:

 std::vector v = ...; v[0]->func(...); v[1]->func(...); 

La surcharge pour les types possibles de T n’est également pas une option.

Quelle est la meilleure solution à cette situation?

Et en dehors du sujet, recommanderiez-vous des références (de préférence des livres) pour ce type de problèmes?

Ce n’est pas chose facile avec C ++. Cela est lié à quelque chose appelé “polymorphism de première classe”, ce qui signifie que ce serait facile si les valeurs en C ++ pouvaient avoir des types polymorphes. Ce n’est pas le cas.

Si une solution générique vous convient (cela signifie que le code f doit être identique pour tous les T ), vous pouvez le faire, mais ce sera une tâche laborieuse.

En gros, vous voudrez remplacer votre paramètre const T &t par un paramètre dont le type ne serait pas générique, mais capturerait “à l’intérieur” tout le comportement f besoin de t s de tous les types possibles.

Par exemple, supposons que T soit un foncteur, que f appelle avec un argument int . Dans ce cas, vous modifierez la déclaration en

  virtual void func(const std::function& t) { ... } 

et les fonctions virtuelles vont commencer à fonctionner. Cependant, cela signifie que l’interface de T devra être corrigée avant de commencer à l’implémenter dans des classes dérivées (c’est-à-dire que si vous changez d’avis et voulez appeler avec un argument de type ostream , vous n’êtes pas ostream ).

Cependant, la création de tels wrappers polymorphes va de facile (comme c’est le cas avec boost::any , boost::function ) à difficile voire impossible ( any_iterator ). Cela dépend beaucoup de ce que vous voulez faire.

Vous ne pouvez pas mélanger un polymorphism de compilation (modèles) avec un polymorphism d’exécution comme celui-ci. Le problème est qu’avec un modèle, le compilateur générera le code à la demande lorsqu’il sera utilisé et, dans votre cas particulier, vous voudrez choisir la fonction membre à instancier selon le type d’exécution de l’object du vecteur.

Si le nombre de types pouvant être utilisés avec les méthodes est limité, vous pouvez fournir différentes surcharges virtuelles. Si vous ne souhaitez pas le faire manuellement, vous pouvez probablement définir une liste de types avec tous les types T, puis utiliser cette liste pour générer les méthodes … mais ce sera affreux à coder et à maintenir.

Je vous recommande d’énoncer les exigences réelles du problème (plutôt que celles de la solution proposée), et les gens seront en mesure de proposer des approches alternatives.