Partial template specialization
Permet de personnaliser les modèles de classe et de variable (depuis C++14) pour une catégorie donnée d'arguments de modèle.
         Table des matières | 
       
Syntaxe
         
          
           template
          
         
         
          
           <
          
         
         
          liste-de-paramètres
         
         
          
           >
          
         
         
          mot-clé-de-classe
         
         
          nom-d'en-tête-de-classe
         
         
          
           <
          
         
         
          liste-d'arguments
         
         
          
           >
          
         
         
          déclaration
         
         | 
        (1) | ||||||||
         
          
           template
          
         
         
          
           <
          
         
         
          liste-de-paramètres
         
         
          
           >
          
         
         
          séquence-de-spécificateurs-de-déclaration
         
         
          déclarateur
         
         
          
           <
          
         
         
          liste-d'arguments
         
         
          
           >
          
         
         
          initialiseur
         
         
         
          (optionnel)
         
         | 
        (2) | (depuis C++14) | |||||||
où class-head-name identifie le nom d'un class template précédemment déclaré et declarator identifie le nom d'un variable template précédemment déclaré (depuis C++14) .
La spécialisation partielle peut être déclarée dans toute portée où son modèle primaire peut être défini (ce qui peut être différent de la portée où le modèle primaire est défini ; comme avec la spécialisation hors-classe d'un modèle membre ). La spécialisation partielle doit apparaître après la déclaration du modèle non spécialisé.
Par exemple,
template<class T1, class T2, int I> class A {}; // modèle principal template<class T, int I> class A<T, T*, I> {}; // #1 : spécialisation partielle où T2 est un pointeur vers T1 template<class T, class T2, int I> class A<T*, T2, I> {}; // #2 : spécialisation partielle où T1 est un pointeur template<class T> class A<int, T*, 5> {}; // #3 : spécialisation partielle où // T1 est int, I est 5, et T2 est un pointeur template<class X, class T, int I> class A<X, T*, I> {}; // #4 : spécialisation partielle où T2 est un pointeur
Exemples de spécialisations partielles dans la bibliothèque standard incluent std::unique_ptr , qui possède une spécialisation partielle pour les types tableau.
La liste des arguments
Les restrictions suivantes s'appliquent à la argument-list d'une spécialisation partielle de modèle :
template<class T1, class T2, int I> class B {}; // primary template template<class X, class Y, int N> class B<X, Y, N> {}; // error
| 
           De plus, la spécialisation doit être plus spécialisée que le template primaire template<int N, typename T1, typename... Ts> struct B; template<typename... Ts> struct B<0, Ts...> {}; // Error: not more specialized  | 
         (depuis C++11) | 
template<int I, int J> struct A {}; template<int I> struct A<I + 5, I * 2> {}; // error, I is not deducible template<int I, int J, int K> struct B {}; template<int I> struct B<I, I * 2, 2> {}; // OK: first parameter is deducible
template<class T, T t> struct C {}; // primary template template<class T> struct C<T, 1>; // error: type of the argument 1 is T, // which depends on the parameter T template<int X, int (*array_ptr)[X]> class B {}; // primary template int array[5]; template<int X> class B<X, &array> {}; // error: type of the argument &array is // int(*)[X], which depends on the parameter X
Recherche de nom
Les spécialisations partielles de modèles ne sont pas trouvées par recherche de nom. Ce n'est que si le modèle principal est trouvé par recherche de nom que ses spécialisations partielles sont considérées. En particulier, une déclaration using qui rend visible un modèle principal rend également visibles les spécialisations partielles :
namespace N { template<class T1, class T2> class Z {}; // modèle primaire } using N::Z; // fait référence au modèle primaire namespace N { template<class T> class Z<T, T*> {}; // spécialisation partielle } Z<int, int*> z; // la recherche de nom trouve N::Z (le modèle primaire), // la spécialisation partielle avec T = int est ensuite utilisée
Ordonnancement partiel
Lorsqu'un modèle de classe ou de variable (since C++14) est instancié, et qu'il existe des spécialisations partielles disponibles, le compilateur doit décider si le modèle principal va être utilisé ou l'une de ses spécialisations partielles.
// given the template A as defined above A<int, int, 1> a1; // no specializations match, uses primary template A<int, int*, 1> a2; // uses partial specialization #1 (T = int, I = 1) A<int, char*, 5> a3; // uses partial specialization #3, (T = char) A<int, char*, 1> a4; // uses partial specialization #4, (X = int, T = char, I = 1) A<int*, int*, 2> a5; // error: matches #2 (T = int, T2 = int*, I= 2) // matches #4 (X = int*, T = int, I = 2) // neither one is more specialized than the other
Informellement, « A est plus spécialisé que B » signifie « A accepte un sous-ensemble des types que B accepte ».
Formellement, pour établir la relation de spécialisation-plus-poussée entre les spécialisations partielles, chacune est d'abord convertie en un modèle de fonction fictif comme suit :
- le premier modèle de fonction a les mêmes paramètres de modèle que la première spécialisation partielle et possède un seul paramètre de fonction, dont le type est une spécialisation de modèle de classe avec tous les arguments de modèle de la première spécialisation partielle
 - le deuxième modèle de fonction a les mêmes paramètres de modèle que la deuxième spécialisation partielle et possède un seul paramètre de fonction dont le type est une spécialisation de modèle de classe avec tous les arguments de modèle de la deuxième spécialisation partielle.
 
Les modèles de fonction sont ensuite classés comme pour la surcharge de modèles de fonction .
template<int I, int J, class T> struct X {}; // modèle principal template<int I, int J> struct X<I, J, int> { static const int s = 1; }; // spécialisation partielle #1 // modèle de fonction fictif pour #1 est // template<int I, int J> void f(X<I, J, int>); #A template<int I> struct X<I, I, int> { static const int s = 2; }; // spécialisation partielle #2 // modèle de fonction fictif pour #2 est // template<int I> void f(X<I, I, int>); #B int main() { X<2, 2, int> x; // #1 et #2 correspondent tous les deux // ordre partiel pour les modèles de fonction : // #A de #B : void(X<I, J, int>) de void(X<U1, U1, int>) : déduction OK // #B de #A : void(X<I, I, int>) de void(X<U1, U2, int>) : déduction échoue // #B est plus spécialisé // #2 est la spécialisation qui est instanciée std::cout << x.s << '\n'; // affiche 2 }
Membres des spécialisations partielles
La liste des paramètres de modèle et la liste des arguments de modèle d'un membre d'une spécialisation partielle doivent correspondre à la liste des paramètres et à la liste des arguments de la spécialisation partielle.
Tout comme pour les membres des modèles primaires, ils ne doivent être définis que s'ils sont utilisés dans le programme.
Les membres des spécialisations partielles ne sont pas liés aux membres du modèle principal.
La spécialisation explicite (complète) d'un membres d'une spécialisation partielle est déclarée de la même manière qu'une spécialisation explicite du modèle primaire.
template<class T, int I> // modèle primaire struct A { void f(); // déclaration de membre }; template<class T, int I> void A<T, I>::f() {} // définition du membre du modèle primaire // spécialisation partielle template<class T> struct A<T, 2> { void f(); void g(); void h(); }; // membre de la spécialisation partielle template<class T> void A<T, 2>::g() {} // spécialisation explicite (complète) // d'un membre de la spécialisation partielle template<> void A<char, 2>::h() {} int main() { A<char, 0> a0; A<char, 2> a2; a0.f(); // OK, utilise la définition du membre du modèle primaire a2.g(); // OK, utilise la définition du membre de la spécialisation partielle a2.h(); // OK, utilise la définition entièrement spécialisée // du membre d'une spécialisation partielle a2.f(); // erreur : aucune définition de f() dans la // spécialisation partielle A<T,2> (le modèle primaire n'est pas utilisé) }
Si un modèle primaire est membre d'un autre modèle de classe, ses spécialisations partielles sont membres du modèle de classe englobant. Si le modèle englobant est instancié, la déclaration de chaque spécialisation partielle membre est également instanciée (de la même manière que les déclarations, mais pas les définitions, de tous les autres membres d'un modèle sont instanciées).
Si le modèle de membre principal est explicitement (entièrement) spécialisé pour une spécialisation donnée (implicite) du modèle de classe englobant, les spécialisations partielles du modèle de membre sont ignorées pour cette spécialisation du modèle de classe englobant.
Si une spécialisation partielle du modèle de membre est explicitement spécialisée pour une spécialisation donnée (implicite) du modèle de classe englobant, le modèle de membre principal et ses autres spécialisations partielles sont toujours considérés pour cette spécialisation du modèle de classe englobant.
template<class T> struct A // classe template englobante { template<class T2> struct B {}; // template membre principal template<class T2> struct B<T2*> {}; // spécialisation partielle du template membre }; template<> template<class T2> struct A<short>::B {}; // spécialisation complète du template membre principal // (ignorera la spécialisation partielle) A<char>::B<int*> abcip; // utilise la spécialisation partielle T2=int A<short>::B<int*> absip; // utilise la spécialisation complète du principal (ignore la partielle) A<char>::B<int> abci; // utilise le template principal
Rapports de défauts
Les rapports de défauts modifiant le comportement suivants ont été appliqués rétroactivement aux normes C++ précédemment publiées.
| DR | Appliqué à | Comportement publié | Comportement corrigé | 
|---|---|---|---|
| CWG 727 | C++98 | 
         les spécialisations partielles et complètes non autorisées
          dans la portée de classe  | 
        autorisées dans toute portée | 
| CWG 1315 | C++98 | 
         le paramètre template ne pouvait pas être utilisé dans les arguments
          template constants autres que les expressions d'identificateur  | 
        les expressions sont acceptables tant qu'elles sont déductibles | 
| CWG 1495 | C++11 | la spécification était peu claire concernant les parameter packs | la spécialisation doit être plus spécialisée | 
| CWG 1711 | C++14 | spécification manquante pour les spécialisations partielles de variable templates | ajouter la prise en charge des variable templates | 
| CWG 1819 | C++98 | portées acceptables pour la définition des spécialisations partielles | 
         permettre aux spécialisations partielles d'être déclarées
          dans la même portée que les templates primaires  | 
       
| CWG 2330 | C++14 | références manquantes aux variable templates | ajouter la prise en charge des variable templates |