Namespaces
Variants

Explicit (full) 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
Template specialization
Parameter packs (C++11)
Miscellaneous

Permet de personnaliser le code du modèle pour un ensemble donné d'arguments de modèle.

Table des matières

Syntaxe

template <> déclaration

N'importe lequel des éléments suivants peut être entièrement spécialisé :

  1. modèle de fonction
  2. modèle de classe
  3. modèle de variable (depuis C++14)
  4. fonction membre d'un modèle de classe
  5. membre de données statique d'un modèle de classe
  6. classe membre d'un modèle de classe
  7. membre énumération d'un modèle de classe
  8. modèle de classe membre d'une classe ou d'un modèle de classe
  9. modèle de fonction membre d'une classe ou d'un modèle de classe
  10. modèle de variable membre d'une classe ou d'un modèle de classe (depuis C++14)

Par exemple,

#include <type_traits>
template<typename T> // primary template
struct is_void : std::false_type {};
template<>           // explicit specialization for T = void
struct is_void<void> : std::true_type {};
int main()
{
    static_assert(is_void<char>::value == false,
        "for any type T other than void, the class is derived from false_type");
    static_assert(is_void<void>::value == true,
        "but when T is void, the class is derived from true_type");
}

En détail

La spécialisation explicite 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 de membre ). La spécialisation explicite doit apparaître après la déclaration du modèle non spécialisé.

namespace N
{
    template<class T> // modèle primaire
    class X { /*...*/ };
    template<>        // spécialisation dans le même espace de noms
    class X<int> { /*...*/ };
    template<class T> // modèle primaire
    class Y { /*...*/ };
    template<>        // déclaration anticipée de la spécialisation pour double
    class Y<double>;
}
template<> // OK : spécialisation dans le même espace de noms
class N::Y<double> { /*...*/ };

La spécialisation doit être déclarée avant la première utilisation qui entraînerait une instanciation implicite, dans chaque unité de traduction où une telle utilisation se produit :

class String {};
template<class T>
class Array { /*...*/ };
template<class T> // modèle primaire
void sort(Array<T>& v) { /*...*/ }
void f(Array<String>& v)
{
    sort(v); // instancie implicitement sort(Array<String>&), 
}            // en utilisant le modèle primaire pour sort()
template<> // ERREUR : spécialisation explicite de sort(Array<String>)
void sort<String>(Array<String>& v); // après l'instanciation implicite

Une spécialisation de modèle qui a été déclarée mais non définie peut être utilisée comme n'importe quel autre type incomplet (par exemple, les pointeurs et références vers celui-ci peuvent être utilisés) :

template<class T> // modèle primaire
class X;
template<>        // spécialisation (déclarée, non définie)
class X<int>;
X<int>* p; // OK : pointeur vers type incomplet
X<int> x;  // erreur : objet de type incomplet

Qu'une spécialisation explicite d'un modèle de fonction ou de variable (since C++14) soit inline / constexpr (since C++11) / constinit / consteval (since C++20) est déterminé par la spécialisation explicite elle-même, indépendamment du fait que le modèle principal soit déclaré avec ce spécificateur. De même, les attributs apparaissant dans la déclaration d'un modèle n'ont aucun effet sur une spécialisation explicite de ce modèle : (since C++11)

template<class T>
void f(T) { /* ... */ }
template<>
inline void f<>(int) { /* ... */ } // OK, inline
template<class T>
inline T g(T) { /* ... */ }
template<>
int g<>(int) { /* ... */ }         // OK, pas inline
template<typename>
[[noreturn]] void h([[maybe_unused]] int i);
template<> void h<int>(int i)
{
    // [[noreturn]] n'a aucun effet, mais [[maybe_unused]] en a
}

Spécialisations explicites des modèles de fonction

Lors de la spécialisation d'un modèle de fonction, ses arguments template peuvent être omis si la déduction d'arguments template peut les fournir à partir des arguments de la fonction :

template<class T>
class Array { /*...*/ };
template<class T> // modèle primaire
void sort(Array<T>& v);
template<>        // spécialisation pour T = int
void sort(Array<int>&);
// pas besoin d'écrire
// template<> void sort<int>(Array<int>&);

Une fonction portant le même nom et la même liste d'arguments qu'une spécialisation n'est pas une spécialisation (voir la surcharge de modèles dans function template ).

Arguments par défaut des fonctions ne peuvent pas être spécifiés dans les spécialisations explicites des modèles de fonctions, des modèles de fonctions membres et des fonctions membres des modèles de classe lorsque la classe est instanciée implicitement.

Une spécialisation explicite ne peut pas être une déclaration friend .

Membres des spécialisations

Lors de la définition d'un membre d'un modèle de classe explicitement spécialisé en dehors du corps de la classe, la syntaxe template <> n'est pas utilisée, sauf s'il s'agit d'un membre d'un modèle de classe membre explicitement spécialisé, qui est spécialisé en tant que modèle de classe, car autrement, la syntaxe exigerait qu'une telle définition commence par template < parameters > requis par le modèle imbriqué

template<typename T>
struct A
{
    struct B {};      // classe membre
    template<class U> // modèle de classe membre
    struct C {};
};
template<> // spécialisation
struct A<int> 
{
    void f(int); // fonction membre d'une spécialisation
};
// template<> non utilisé pour un membre d'une spécialisation
void A<int>::f(int) { /* ... */ }
template<> // spécialisation d'une classe membre
struct A<char>::B
{
    void f();
};
// template<> non utilisé non plus pour un membre d'une classe membre spécialisée
void A<char>::B::f() { /* ... */ }
template<> // spécialisation d'un modèle de classe membre
template<class U>
struct A<char>::C
{
    void f();
};
// template<> est utilisé lors de la définition d'un membre d'un
// modèle de classe membre explicitement spécialisé en tant que modèle de classe
template<>
template<class U>
void A<char>::C<U>::f() { /* ... */ }


Une spécialisation explicite d'un membre de données statique d'un modèle est une définition si la déclaration inclut un initialiseur ; sinon, c'est une déclaration. Ces définitions doivent utiliser des accolades pour l'initialisation par défaut :

template<>
X Q<int>::x;    // déclaration d'un membre statique
template<>
X Q<int>::x (); // erreur : déclaration de fonction
template<>
X Q<int>::x {}; // définition d'un membre statique initialisé par défaut

Un membre ou un patron de membre d'un patron de classe peut être explicitement spécialisé pour une instanciation implicite donnée du patron de classe, même si le membre ou le patron de membre est défini dans la définition du patron de classe.

template<typename T>
struct A
{
    void f(T);         // membre, déclaré dans le template primaire
    void h(T) {}       // membre, défini dans le template primaire
    template<class X1> // template de membre
    void g1(T, X1);
    template<class X2> // template de membre
    void g2(T, X2);
};
// spécialisation d'un membre
template<>
void A<int>::f(int);
// spécialisation de membre OK même si définie dans la classe
template<>
void A<int>::h(int) {}
// définition de template de membre hors classe
template<class T>
template<class X1>
void A<T>::g1(T, X1) {}
// spécialisation de template de membre
template<>
template<class X1>
void A<int>::g1(int, X1);
// spécialisation de template de membre
template<>
template<>
void A<int>::g2<char>(int, char); // pour X2 = char
// même chose, en utilisant la déduction d'arguments de template (X1 = char)
template<> 
template<>
void A<int>::g1(int, char);

Un membre ou un patron de membre peut être imbriqué dans plusieurs patrons de classe englobants. Dans une spécialisation explicite pour un tel membre, il y a un template <> pour chaque patron de classe englobant qui est explicitement spécialisé.

template<class T1>
struct A
{
    template<class T2>
    struct B
    {
        template<class T3>
        void mf();
    };
};
template<>
struct A<int>;
template<>
template<>
struct A<char>::B<double>;
template<>
template<>
template<>
void A<char>::B<char>::mf<double>();
**Note:** Le code C++ présenté ne contient aucun texte à traduire en français, car il s'agit exclusivement de code source C++ qui doit être préservé selon vos instructions. Tous les éléments (mots-clés, noms de types, structures, templates) sont des termes spécifiques au C++ qui ne doivent pas être traduits.

Dans une telle déclaration imbriquée, certains niveaux peuvent rester non spécialisés (sauf qu'on ne peut pas spécialiser un modèle de membre de classe dans la portée de l'espace de noms si sa classe englobante n'est pas spécialisée). Pour chacun de ces niveaux, la déclaration nécessite template < arguments > , car ces spécialisations sont elles-mêmes des modèles :

template<class T1>
class A
{
    template<class T2>
    class B
    {
        template<class T3> // template membre
        void mf1(T3);
        void mf2();        // membre non-template
    };
};
// spécialisation
template<>        // pour le A spécialisé
template<class X> // pour le B non spécialisé
class A<int>::B
{
    template<class T>
    void mf1(T);
};
// spécialisation
template<>        // pour le A spécialisé
template<>        // pour le B spécialisé
template<class T> // pour le mf1 non spécialisé
void A<int>::B<double>::mf1(T t) {}
// ERREUR : B<double> est spécialisé et est un template membre, donc son A englobant
// doit également être spécialisé
template<class Y>
template<>
void A<Y>::B<double>::mf2() {}

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 531 C++98 la syntaxe de définition des membres des spécialisations
explicites dans la portée de l'espace de noms n'était pas spécifiée
spécifiée
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 730 C++98 les modèles de membres des classes non modèles
ne pouvaient pas être entièrement spécialisés
autorisé
CWG 2478 C++20 il n'était pas clair si le constinit et le consteval du
modèle principal sont reportés dans ses spécialisations explicites
non reportés
CWG 2604 C++11 il n'était pas clair si les attributs du modèle
principal sont reportés dans ses spécialisations explicites
non reportés

Voir aussi