Template arguments
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
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) |
|
(depuis C++11)
(jusqu'en C++20) |
|
(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 :
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
|
(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
PouA. -
Chaque modèle de fonction a un seul paramètre de fonction dont le type est une spécialisation de
Xavec 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èlePPdans la liste des paramètres de modèle du modèle de fonction, un argument de modèle correspondantAAest formé. SiPPdéclare un pack de paramètres, alorsAAest l'expansion de packPP...; sinon, (depuis C++11)AAest l'expression-idPP.
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) |
|
(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
|
Cette section est incomplète
Raison : aucun 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é |