Spécialisation de classe basée sur un modèle où l’argument de modèle est un modèle

Je me demande si quelque chose de semblable à cela est possible. En gros, j’ai une classe basée sur des modèles qui prend parfois des objects de classes basées sur des modèles. Je voudrais le spécialiser (ou juste une fonction membre) pour une classe spécifique basée sur un modèle, mais la forme “générique” de cette classe.

template class SomeRandomClass { //put something here }; template class MyTemplateClass { void DoSomething(T & t) { //...something } }; template void MyTemplateClass< SomeRandomClass >::DoSomething(SomeRandomClass & t) { //something specialized happens here } 

Remplacer les points d’interrogation par des types appropriés (double, etc.) fonctionne, mais j’aimerais qu’il rest générique. Je ne sais pas quoi mettre là, car aucun type n’aurait été défini. J’ai regardé autour de moi et appris des parameters de modèle de gabarit. J’ai essayé diverses combinaisons en vain. Merci pour l’aide!

Il est possible de spécialiser la classe comme ça

 template <> template  class MyTemplateClass  > { void DoSomething(SomeRandomClass& t) { /* something */ } }; 

Il n’est pas possible de spécialiser uniquement la méthode membre, car la spécialisation concerne l’ensemble de la classe et vous devez définir une nouvelle classe. Vous pouvez cependant faire

 template <> template  class MyTemplateClass  > { void DoSomething(SomeRandomClass& t); }; template <> template  void MyTemplateClass >::DoSomething(SomeRandomClass& t) { // something } 

diviser la déclaration et la définition.

Tout ce que vous avez à faire est simplement de définir un modèle pour ce que vous voulez conserver générique. Prenant ce que vous avez commencé avec:

 template void MyTemplateClass< SomeRandomClass >::DoSomething(SomeRandomClass & t) { //something specialized happens here } 

MODIFIER:

Si vous souhaitez uniquement conserver une partie du générique SomeRandomClass , vous pouvez également:

 template void MyTemplateClass< SomeRandomClass >::DoSomething(SomeRandomClass & t) { //something specialized happens here } 

Je ne suis pas tout à fait sûr de savoir pourquoi @Ryan Calhoun s’est spécialisé, mais voici un exemple plus concis:

 // class we want to specialize with later on template struct SomeRandomClass { int myInt = 0; }; // non-specialized class template struct MyTemplateClass { void DoSomething(T & t) { std::cout << "Not specialized" << std::endl; } }; // specialized class template struct MyTemplateClass< SomeRandomClass > { void DoSomething(SomeRandomClass & t) { std::cout << "Specialized" << std::endl; } }; 

Vous pouvez voir que vous n'avez pas besoin de la syntaxe redondante utilisée dans la réponse acceptée:

 template<> template 

Démo de travail


Alternative

Vous pouvez utiliser type_traits et tag-dispatch dans votre classe non spécialisée pour spécialiser uniquement la fonction.

Faisons d'abord un concept pour is_random_class :

 // concept to test for whether some type is SomeRandomClass template struct is_random_class : std::false_type{}; template struct is_random_class> : std::true_type{}; 

Et ensuite, déclarons à nouveau notre MyTemplateClass , mais cette fois-ci non modélisé (car nous ne sums pas spécialisés), nous l'appellerons donc MyNonTemplatedClass :

 class MyNonTemplatedClass { public: template void DoSomething(T & t) { DoSomethingHelper(t, typename is_random_class::type()); } // ... 

Remarquez comment DoSomething est maintenant DoSomething modèle et appelle en réalité un assistant au lieu de mettre en œuvre la logique elle-même?

Décomposons la ligne:

 DoSomethingHelper(t, typename is_random_class::type()); 
  • t est comme avant; nous passons l'argument de type T&
  • typename is_random_class::type()
    • is_random_class est notre concept, et comme il dérive de std::true_type ou de std::false_type , un ::type défini dans la classe (Google pour "traits de type").
    • ::type() 'instancie' le type spécifié par is_random_class::type . Je le dis entre guillemets parce que nous allons vraiment jeter cela comme nous le verrons plus tard
    • typename est obligatoire car le compilateur ne sait pas que is_random_clas::type nomme en réalité un type.

Nous sums maintenant prêts à examiner le rest de MyNonTemplatedClass :

  private: //use tag dispatch. If the comstackr is smart it won't actually try to instantiate the second param template void DoSomethingHelper(T&t, std::true_type) { std::cout << "Called DoSomething with SomeRandomClass whose myInt member has value " << t.myInt << std::endl; } template void DoSomethingHelper(T&t, std::false_type) { std::cout << "Called DoSomething with a type that is not SomeRandomClass\n"; } }; 

Démo complète de travail v2 ici

Notez que nos fonctions d'assistance portent le même nom, mais sont surchargées sur le type du second paramètre. Nous ne donnons pas de nom au paramètre car nous n'en avons pas besoin, et nous espérons que le compilateur l’optimisera tout en appelant la fonction appropriée.

Notre concept force DoSomethingHelper(T&t, std::true_type) uniquement si T est de type SomeRandomClass et appelle l'autre pour tout autre type.

L'avantage de l'envoi de tags

Le principal avantage de l'envoi de balises est qu'il n'est pas nécessaire de spécialiser toute votre classe si vous voulez uniquement spécialiser une seule fonction de cette classe.

La répartition des balises se produira au moment de la compilation, ce qui ne se produirait pas si vous tentiez de créer une twig sur le concept uniquement dans la fonction DoSomething .

Edit: ceci est une réponse correcte à une question différente.

L’utilisation du nom de type T deux resockets confond un peu le problème, car ils sont compilés séparément et ne sont connectés d’aucune façon.

Vous pouvez surcharger la méthode pour prendre un paramètre basé sur un modèle:

 template  class MyTemplateClass { void DoSomething(T& t) { } template  void DoSomething(SomeRandomClass<& r) { } }; 

Ceci mappe U et V dans la nouvelle méthode sur T' et S' dans SomeRandomClass . Dans cette configuration, U ou V peuvent être du même type que T , mais ils ne doivent pas nécessairement l’être. En fonction de votre compilateur, vous devriez pouvoir faire

 MyTemplateClass mine; SomeRandomClass random; // note: nevermind the non-const ref on the ssortingng literal here... mine.DoSomething("hello world"); mine.DoSomething(random); 

et l’appel basé sur un modèle sera sélectionné en tant que surcharge correspondante sans avoir à spécifier explicitement les types.

Modifier:

Faire avec avec des modèles de spécialisation ne fait aucune différence pour la surcharge de DoSomething . Si vous spécialisez la classe comme suit

 template <> class SomeRandomClass  { // something here... }; 

alors la surcharge ci-dessus dévorera volontiers cette implémentation spécialisée. Assurez-vous simplement que les interfaces du modèle spécialisé et du modèle par défaut correspondent.

Si ce que vous voulez, c’est spécialiser DoSomething pour prendre une paire de types spécifique pour SomeRandomClass alors vous avez déjà perdu la généralité … c’est ce qu’est la spécialisation.