Namespaces
Variants

Template arguments

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

Pour qu'un template soit instancié, chaque template parameter doit être remplacé par un argument de template correspondant. Les arguments sont soit explicitement fournis, déduits ou par défaut.

Chaque paramètre dans template-parameter-list (voir syntaxe d'identifiant de template ) appartient à l'une des catégories suivantes :

  • argument de modèle constant
  • argument de modèle de type
  • argument de modèle de modèle

Table des matières

Arguments de modèle constants

Également appelés non-type template arguments (voir ci-dessous ).

L'argument de modèle qui peut être utilisé avec un paramètre de modèle constant peut être toute expression manifestement évaluée comme constante .

(until C++11)

L'argument de modèle qui peut être utilisé avec un paramètre de modèle constant peut être toute clause d'initialisation . Si la clause d'initialisation est une expression, elle doit être manifestement évaluée comme constante .

(since C++11)

Étant donné le type de la déclaration du paramètre de template constant comme T et l'argument de template fourni pour le paramètre comme E .

La déclaration inventée T x = E ; doit satisfaire les contraintes sémantiques pour la définition d'une constexpr variable avec durée de stockage statique .

(depuis C++20)

Si T contient un type substitut , ou est un substitut pour un type de classe déduit , le type du paramètre template est le type déduit pour la variable x dans la déclaration inventée T x = E ; .

Si un type de paramètre déduit n'est pas un type structurel , le programme est mal formé.

Pour les packs de paramètres template constants dont le type utilise un type substitut, le type est déduit indépendamment pour chaque argument template et n'a pas besoin de correspondre.

(depuis C++17)
template<auto n>
struct B { /* ... */ };
B<5> b1;   // OK : le type du paramètre template constant est int
B<'a'> b2; // OK : le type du paramètre template constant est char
B<2.5> b3; // erreur (jusqu'à C++20) : le type du paramètre template constant ne peut pas être double
// Placeholder de type de classe déduit C++20, les arguments template de classe sont déduits au
// site d'appel
template<std::array arr>
void f();
f<std::array<double, 8>{}>();
template<auto...>
struct C {};
C<'C', 0, 2L, nullptr> x; // OK

La valeur d'un paramètre de modèle constant P de type (éventuellement déduit) (depuis C++17) T est déterminée à partir de son argument de modèle A comme suit :

(jusqu'en C++11)
  • Si A est une expression :
  • Sinon ( A est une liste d'initialisation entre accolades), une variable temporaire constexpr T v = A ; est introduite. La valeur de P est celle de v .
  • La durée de vie de v se termine immédiatement après son initialisation.
(depuis C++11)
(jusqu'en C++20)
  • Si T n'est pas un type classe et A est une expression :
  • Sinon ( T est un type classe ou A est une liste d'initialisation entre accolades), une variable temporaire constexpr T v = A ; est introduite.
  • La durée de vie de v se termine immédiatement après son initialisation et celle de P .
  • Si l'initialisation de P satisfait l'une des conditions suivantes, le programme est mal formé :
  • Sinon, la valeur de P est celle de v .
(depuis C++20)
template<int i>
struct C { /* ... */ };
C<{42}> c1; // OK
template<auto n>
struct B { /* ... */ };
struct J1
{
    J1* self = this;
};
B<J1{}> j1; // erreur : l'initialisation de l'objet paramètre de modèle
            //        n'est pas une expression constante
struct J2
{
    J2 *self = this;
    constexpr J2() {}
    constexpr J2(const J2&) {}
};
B<J2{}> j2; // erreur : l'objet paramètre de modèle n'est pas
            //        équivalent-argument-de-modèle au temporaire introduit

Les limitations suivantes s'appliquent lors de l'instanciation de templates ayant des paramètres template constants :

  • Pour les types entiers et arithmétiques, l'argument template fourni lors de l'instanciation doit être une expression constante convertie du type du paramètre template (ainsi certaines conversions implicites s'appliquent).
  • Pour les pointeurs vers des objets, les arguments template doivent désigner l'adresse d'un objet complet avec une durée de stockage statique et une liaison (soit interne, soit externe), ou une expression constante qui évalue à la valeur de pointeur nul appropriée ou std::nullptr_t (depuis C++11) .
  • Pour les pointeurs vers des fonctions, les arguments valides sont des pointeurs vers des fonctions avec liaison (ou des expressions constantes qui évaluent à des valeurs de pointeur nul).
  • Pour les paramètres de référence lvalue, l'argument fourni lors de l'instanciation ne peut pas être un temporaire, une lvalue sans nom, ou une lvalue nommée sans liaison (en d'autres termes, l'argument doit avoir une liaison).
  • Pour les pointeurs vers des membres, l'argument doit être un pointeur vers un membre exprimé comme & Class :: Member ou une expression constante qui évalue à un pointeur nul ou std::nullptr_t (depuis C++11) .

En particulier, cela implique que les littéraux de chaîne, les adresses d'éléments de tableau et les adresses de membres non statiques ne peuvent pas être utilisés comme arguments template pour instancier des templates dont les paramètres template constants correspondants sont des pointeurs vers des objets.

(jusqu'à C++17)

les paramètres template constants de type référence ou pointeur et les membres de données non statiques de type référence ou pointeur dans un paramètre template constant de type classe et ses sous-objets (depuis C++20) ne peuvent pas se référer à/être l'adresse de

  • un objet temporaire (y compris celui créé pendant l' initialisation de référence );
  • un littéral de chaîne ;
  • le résultat de typeid ;
  • la variable prédéfinie __func__ ;
  • ou un sous-objet (y compris un membre de classe non statique, un sous-objet de base, ou un élément de tableau) de l'un des éléments ci-dessus (depuis C++20) .
(depuis C++17)
template<const int* pci>
struct X {};
int ai[10];
X<ai> xi; // OK : conversion tableau vers pointeur et conversion de qualification cv
struct Y {};
template<const Y& b>
struct Z {};
Y y;
Z<y> z;   // OK : aucune conversion
template<int (&pa)[5]>
struct W {};
int b[5];
W<b> w;   // OK : aucune conversion
void f(char);
void f(int);
template<void (* pf)(int)>
struct A {};
A<&f> a;  // OK : la résolution de surcharge sélectionne f(int)
template<class T, const char* p>
class X {};
X<int, "Studebaker"> x1; // erreur : littéral de chaîne comme argument de template
template<int* p>
class X {};
int a[10];
struct S
{
    int m;
    static int s;
} s;
X<&a[2]> x3; // erreur (jusqu'à C++20) : adresse d'un élément de tableau
X<&s.m> x4;  // erreur (jusqu'à C++20) : adresse d'un membre non statique
X<&s.s> x5;  // OK : adresse d'un membre statique
X<&S::s> x6; // OK : adresse d'un membre statique
template<const int& CRI>
struct B {};
B<1> b2;     // erreur : un temporaire serait requis pour l'argument de template
int c = 1;
B<c> b1;     // OK

Arguments de modèle de type

Un argument de modèle pour un paramètre de modèle de type doit être un type-id , qui peut désigner un type incomplet :

template<typename T>
class X {}; // modèle de classe
struct A;            // type incomplet
typedef struct {} B; // alias de type vers un type anonyme
int main()
{
    X<A> x1;  // OK : 'A' désigne un type
    X<A*> x2; // OK : 'A*' désigne un type
    X<B> x3;  // OK : 'B' désigne un type
}

Arguments de modèle de modèle

Un argument de template pour un paramètre template-template doit être une expression-id qui nomme un template de classe ou un alias de template.

Lorsque l'argument est un template de classe, seul le template principal est pris en compte lors de la correspondance avec le paramètre. Les spécialisations partielles, s'il y en a, ne sont considérées que lorsqu'une spécialisation basée sur ce paramètre template de template est instanciée.

template<typename T> // modèle primaire
class A { int x; };
template<typename T> // spécialisation partielle
class A<T*> { long x; };
// classe template avec un paramètre template template V
template<template<typename> class V>
class C
{
    V<int> y;  // utilise le modèle primaire
    V<int*> z; // utilise la spécialisation partielle
};
C<A> c; // c.y.x a le type int, c.z.x a le type long

Pour faire correspondre un argument template template A à un paramètre template template P , P doit être au moins aussi spécialisé que A (voir ci-dessous). Si la liste de paramètres de P inclut un parameter pack , zéro ou plusieurs paramètres templates (ou parameter packs) de la liste de paramètres template de A sont appariés par celui-ci. (depuis C++11)

Formellement, un paramètre-template de template P est au moins aussi spécialisé qu'un argument-template de template A si, en considérant la réécriture suivante en deux templates de fonction, le template de fonction correspondant à P est au moins aussi spécialisé que le template de fonction correspondant à A selon les règles de classement partiel pour les templates de fonction . Étant donné une classe template inventée X avec la liste de paramètres template de A (incluant les arguments par défaut) :

  • Chacune des deux modèles de fonction a les mêmes paramètres de modèle, respectivement, que P ou A .
  • Chaque modèle de fonction a un seul paramètre de fonction dont le type est une spécialisation de X avec des arguments de modèle correspondant aux paramètres de modèle du modèle de fonction respectif où, pour chaque paramètre de modèle PP dans la liste des paramètres de modèle du modèle de fonction, un argument de modèle correspondant AA est formé. Si PP déclare un pack de paramètres, alors AA est l'expansion de pack PP... ; sinon, (depuis C++11) AA est l'expression-id PP .

Si la réécriture produit un type invalide, alors P n'est pas au moins aussi spécialisé que A .

template<typename T>
struct eval;                     // modèle principal
template<template<typename, typename...> class TT, typename T1, typename... Rest>
struct eval<TT<T1, Rest...>> {}; // spécialisation partielle de eval
template<typename T1> struct A;
template<typename T1, typename T2> struct B;
template<int N> struct C;
template<typename T1, int N> struct D;
template<typename T1, typename T2, int N = 17> struct E;
eval<A<int>> eA;        // OK : correspond à la spécialisation partielle de eval
eval<B<int, float>> eB; // OK : correspond à la spécialisation partielle de eval
eval<C<17>> eC;         // erreur : C ne correspond pas à TT dans la spécialisation partielle
                        // car le premier paramètre de TT est un
                        // paramètre de modèle de type, tandis que 17 ne nomme pas un type
eval<D<int, 17>> eD;    // erreur : D ne correspond pas à TT dans la spécialisation partielle
                        // car le deuxième paramètre de TT est un
                        // paramètre de type variadique, tandis que 17 ne nomme pas un type
eval<E<int, float>> eE; // erreur : E ne correspond pas à TT dans la spécialisation partielle
                        // car le troisième paramètre (par défaut) de E est une constante

Avant l'adoption de P0522R0 , chacun des paramètres de template de A devait correspondre exactement aux paramètres de template correspondants de P . Cela empêchait l'acceptation de nombreux arguments de template raisonnables.

Bien qu'il ait été signalé très tôt ( CWG#150 ), au moment où il a été résolu, les modifications ont été appliquées au document de travail C++17 et la résolution est devenue une fonctionnalité de facto de C++17. De nombreux compilateurs le désactivent par défaut :

  • GCC le désactive dans tous les modes de langage antérieurs à C++17 par défaut, il ne peut être activé qu'en définissant un indicateur du compilateur dans ces modes.
  • Clang le désactive dans tous les modes de langage par défaut, il ne peut être activé qu'en définissant un indicateur du compilateur.
  • Microsoft Visual Studio le traite comme une fonctionnalité normale de C++17 et ne l'active que dans les modes de langage C++17 et ultérieurs (c'est-à-dire aucune prise en charge dans le mode de langage C++14, qui est le mode par défaut).
template<class T> class A { /* ... */ };
template<class T, class U = T> class B { /* ... */ };
template<class... Types> class C { /* ... */ };
template<template<class> class P> class X { /* ... */ };
X<A> xa; // OK
X<B> xb; // OK après P0522R0
         // Erreur précédemment : pas une correspondance exacte
X<C> xc; // OK après P0522R0
         // Erreur précédemment : pas une correspondance exacte
template<template<class...> class Q> class Y { /* ... */ };
Y<A> ya; // OK
Y<B> yb; // OK
Y<C> yc; // OK
template<auto n> class D { /* ... */ };   // note : C++17
template<template<int> class R> class Z { /* ... */ };
Z<D> zd; // OK après P0522R0 : le paramètre de template
         // est plus spécialisé que l'argument de template
template<int> struct SI { /* ... */ };
template<template<auto> class> void FA(); // note : C++17
FA<SI>(); // Erreur

Équivalence des arguments de template

L'équivalence des arguments de template est utilisée pour déterminer si deux identifiants de template sont identiques.

Deux valeurs sont template-argument-equivalent si elles sont de même type et si l'une des conditions suivantes est satisfaite :

  • Ils sont de type intégral ou énumération et leurs valeurs sont identiques.
  • Ils sont de type pointeur et ils ont la même valeur de pointeur.
  • Ils sont de type pointeur-vers-membre et ils se réfèrent au même membre de classe ou sont tous deux la valeur de pointeur de membre nul.
  • Ils sont de type référence lvalue et ils se réfèrent au même objet ou fonction.
(depuis C++11)
  • Ils sont de type à virgule flottante et leurs valeurs sont identiques.
  • Ils sont de type tableau (auquel cas les tableaux doivent être des objets membres d'une classe/union) et leurs éléments correspondants sont équivalents en tant qu'arguments de modèle.
  • Ils sont de type union et soit ils n'ont aucun membre actif, soit ils ont le même membre actif et leurs membres actifs sont équivalents en tant qu'arguments de modèle.
  • Ils sont d'un type de fermeture lambda.
  • Ils sont d'un type classe non-union et leurs sous-objets directs et membres de référence correspondants sont équivalents en tant qu'arguments de modèle.
(depuis C++20)

Résolution d'ambiguïté

Si un argument de template peut être interprété à la fois comme un type-id et une expression, il est toujours interprété comme un type-id, même si le paramètre de template correspondant est constant :

template<class T>
void f(); // #1
template<int I>
void f(); // #2
void g()
{
    f<int()>(); // "int()" est à la fois un type et une expression,
                // appelle #1 car il est interprété comme un type
}

Notes

Avant C++26, les arguments de modèle constants étaient appelés arguments de modèle non-type dans la terminologie standard. La terminologie a été modifiée par P2841R6 / PR #7587 .

Macro de test de fonctionnalité Valeur Std Fonctionnalité
__cpp_template_template_args 201611L (C++17)
(DR)
Correspondance des arguments de modèle de modèle
__cpp_nontype_template_args 201411L (C++17) Autoriser l'évaluation constante pour tous les arguments de modèle constants
201911L (C++20) Types de classes et types à virgule flottante dans les paramètres de modèle constants

Exemple

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 tel que publié Comportement correct
CWG 150
( P0522R0 )
C++98 les arguments template-template devaient correspondre exactement
aux listes de paramètres des paramètres template-template
les plus spécialisés
également autorisés
CWG 354 C++98 les valeurs de pointeur nul ne pouvaient pas être des arguments template constants autorisé
CWG 1398 C++11 les arguments template constants ne pouvaient pas avoir le type std::nullptr_t autorisé
CWG 1570 C++98 les arguments template constants pouvaient désigner des adresses de sous-objets non autorisé
P2308R1 C++11
C++20
1. l'initialisation par liste n'était pas autorisée pour
les arguments template constants (C++11)
2. il n'était pas clair comment les paramètres template
constants de types classe sont initialisés (C++20)
1. autorisé
2. clarifié