Namespaces
Variants

Partial template specialization

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

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)

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 :

1) La liste d'arguments ne peut pas être identique à la liste d'arguments non spécialisée (elle doit spécialiser quelque chose) :
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)
2) Les arguments par défaut ne peuvent pas apparaître dans la liste d'arguments
3) Si un argument est une expansion de pack, il doit être le dernier argument dans la liste
4) Les expressions d'argument constant peuvent utiliser des paramètres de template tant que le paramètre apparaît au moins une fois en dehors d'un contexte non déduit (notez que seuls clang et gcc 12 prennent actuellement en charge cette fonctionnalité) :
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
5) Un argument de modèle constant ne peut pas spécialiser un paramètre de modèle dont le type dépend d'un paramètre de la spécialisation :
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.

1) Si une seule spécialisation correspond aux arguments du modèle, cette spécialisation est utilisée
2) Si plus d'une spécialisation correspond, les règles de classement partiel sont utilisées pour déterminer quelle spécialisation est la plus spécialisée. La spécialisation la plus spécialisée est utilisée, si elle est unique (si elle n'est pas unique, le programme ne peut pas être compilé)
3) Si aucune spécialisation ne correspond, le modèle principal est utilisé
// 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

Voir aussi