Template parameters
Chaque template est paramétré par un ou plusieurs paramètres de template.
Chaque paramètre dans template-parameter-list (voir syntaxe de déclaration de template ) appartient à l'une des catégories suivantes :
- paramètre de modèle constant
- paramètre de modèle de type
- paramètre de modèle de modèle
Table des matières |
Paramètre de modèle constant
Également appelé non-type template parameter (voir ci-dessous ).
| type name (facultatif) | (1) | ||||||||
type
name
(facultatif)
=
default
|
(2) | ||||||||
type
...
name
(facultatif)
|
(3) | (depuis C++11) | |||||||
| type | - |
un des types suivants :
|
||||
| name | - | le nom du paramètre de template constant | ||||
| default | - | l' argument de template par défaut |
Un
type structurel
est l'un des types suivants (éventuellement qualifié cv, les qualificateurs sont ignorés) :
- type référence lvalue (vers objet ou vers fonction) ;
- un type intégral ;
- un type pointeur (vers objet ou vers fonction) ;
- un type pointeur vers membre (vers membre objet ou vers membre fonction) ;
- un type énumération ;
| (depuis C++11) |
|
(depuis C++20) |
Les types de tableau et de fonction peuvent être écrits dans une déclaration de modèle, mais ils sont automatiquement remplacés par un pointeur vers un objet et un pointeur vers une fonction selon le cas.
Lorsque le nom d'un paramètre de modèle constant est utilisé dans une expression au sein du corps du modèle de classe, il s'agit d'une prvalue non modifiable, sauf si son type était un type référence lvalue , ou sauf si son type est un type classe (depuis C++20) .
Un paramètre de template de la forme
class
Foo
n'est pas un paramètre de template constant sans nom de type
Foo
, même si par ailleurs
class
Foo
est un
spécificateur de type élaboré
et que
class
Foo x
;
déclare
x
comme étant de type
Foo
.
|
Un
identifiant
qui nomme un paramètre de template constant de type classe
struct A { friend bool operator==(const A&, const A&) = default; }; template<A a> void f() { &a; // OK const A& ra = a, &rb = a; // Les deux liés au même objet paramètre de template assert(&ra == &rb); // passe } |
(depuis C++20) |
Paramètre de modèle de type
| type-parameter-key name (facultatif) | (1) | ||||||||
type-parameter-key
name
(facultatif)
=
default
|
(2) | ||||||||
type-parameter-key
...
name
(facultatif)
|
(3) | (depuis C++11) | |||||||
| type-constraint name (facultatif) | (4) | (depuis C++20) | |||||||
type-constraint
name
(facultatif)
=
default
|
(5) | (depuis C++20) | |||||||
type-constraint
...
name
(facultatif)
|
(6) | (depuis C++20) | |||||||
| type-parameter-key | - |
soit
typename
ou
class
. Il n'y a aucune différence entre ces mots-clés dans une déclaration de paramètre de modèle de type
|
| type-constraint | - | soit le nom d'un concept ou le nom d'un concept suivi d'une liste d'arguments de modèle (entre chevrons). Dans les deux cas, le nom du concept peut être optionnellement qualifié |
| name | - | le nom du paramètre de modèle de type |
| default | - | l' argument de modèle par défaut |
template<class T> class My_vector { /* ... */ };
template<class T = void> struct My_op_functor { /* ... */ };
template<typename... Ts> class My_tuple { /* ... */ };
template<My_concept T> class My_constrained_vector { /* ... */ };
template<My_concept T = void> class My_constrained_op_functor { /* ... */ };
template<My_concept... Ts> class My_constrained_tuple { /* ... */ };
Le nom du paramètre est facultatif :
// Déclarations des modèles montrés ci-dessus : template<class> class My_vector; template<class = void> struct My_op_functor; template<typename...> class My_tuple;
Dans le corps de la déclaration du template, le nom d'un paramètre de type est un typedef-name qui alias le type fourni lors de l'instanciation du template.
|
Chaque paramètre contraint
template<typename T> concept C1 = true; template<typename... Ts> // variadic concept concept C2 = true; template<typename T, typename U> concept C3 = true; template<C1 T> struct s1; // constraint-expression is C1<T> template<C1... T> struct s2; // constraint-expression is (C1<T> && ...) template<C2... T> struct s3; // constraint-expression is (C2<T> && ...) template<C3<int> T> struct s4; // constraint-expression is C3<T, int> template<C3<int>... T> struct s5; // constraint-expression is (C3<T, int> && ...) |
(depuis C++20) |
Paramètre template template
template
<
liste-de-paramètres
>
clé-type-paramètre
nom
(optionnel)
|
(1) | ||||||||
template
<
liste-de-paramètres
>
clé-type-paramètre
nom
(optionnel)
=
valeur-par-défaut
|
(2) | ||||||||
template
<
liste-de-paramètres
>
clé-type-paramètre
...
nom
(optionnel)
|
(3) | (depuis C++11) | |||||||
| type-parameter-key | - |
class
ou
typename
(depuis C++17)
|
Dans le corps de la déclaration du template, le nom de ce paramètre est un nom-de-template (et nécessite des arguments pour être instancié).
template<typename T> class my_array {}; // deux paramètres de type template et un paramètre template template : template<typename K, typename V, template<typename> typename C = my_array> class Map { C<K> key; C<V> value; };
Résolution des noms pour les paramètres de template
Le nom d'un paramètre de modèle n'est pas autorisé à être redéclaré dans sa portée (y compris les portées imbriquées). Un paramètre de modèle ne peut pas avoir le même nom que le nom du modèle.
template<class T, int N> class Y { int T; // erreur : paramètre de template redéclaré void f() { char T; // erreur : paramètre de template redéclaré } }; template<class X> class X; // erreur : paramètre de template redéclaré
Dans la définition d'un membre d'un modèle de classe qui apparaît en dehors de la définition du modèle de classe, le nom d'un membre du modèle de classe masque le nom d'un paramètre de modèle de tout modèle de classe englobant, mais pas un paramètre de modèle du membre si le membre est un modèle de classe ou de fonction.
template<class T> struct A { struct B {}; typedef void C; void f(); template<class U> void g(U); }; template<class B> void A<B>::f() { B b; // B de A, pas le paramètre template } template<class B> template<class C> void A<B>::g(C) { B b; // B de A, pas le paramètre template C c; // le paramètre template C, pas C de A }
Dans la définition d'un membre d'un modèle de classe qui apparaît en dehors de l'espace de noms contenant la définition du modèle de classe, le nom d'un paramètre de modèle masque le nom d'un membre de cet espace de noms.
namespace N { class C {}; template<class T> class B { void f(T); }; } template<class C> void N::B<C>::f(C) { C b; // C est le paramètre template, pas N::C }
Dans la définition d'un modèle de classe ou dans la définition d'un membre d'un tel modèle qui apparaît en dehors de la définition du modèle, pour chaque classe de base non- dépendante , si le nom de la classe de base ou le nom d'un membre de la classe de base est identique au nom d'un paramètre de modèle, le nom de la classe de base ou le nom du membre masque le nom du paramètre de modèle.
struct A { struct B {}; int C; int Y; }; template<class B, class C> struct X : A { B b; // B de A C b; // erreur : C de A n'est pas un nom de type };
Arguments de modèle par défaut
Les arguments par défaut des modèles sont spécifiés dans les listes de paramètres après le = signe. Les valeurs par défaut peuvent être spécifiées pour tout type de paramètre de modèle (type, constante ou template) , mais pas pour les parameter packs (depuis C++11) .
Si une valeur par défaut est spécifiée pour un paramètre de template d'un template de classe primaire , d'un template de variable primaire, (depuis C++14) ou d'un template d'alias, chaque paramètre de template suivant doit avoir un argument par défaut , sauf le tout dernier qui peut être un parameter pack de template (depuis C++11) . Dans un template de fonction, il n'y a aucune restriction sur les paramètres qui suivent un paramètre par défaut , et un parameter pack peut être suivi par d'autres paramètres de type seulement s'ils ont des valeurs par défaut ou peuvent être déduits des arguments de la fonction (depuis C++11) .
Les paramètres par défaut ne sont pas autorisés
- dans la définition hors-classe d'un membre d'un modèle de classe (ils doivent être fournis dans la déclaration à l'intérieur du corps de la classe). Notez que les modèles de membres des classes non modèles peuvent utiliser des paramètres par défaut dans leurs définitions hors-classe (voir bogue GCC 53856 )
- dans les déclarations de modèles de classe amis
| (jusqu'à C++11) |
|
Dans une déclaration de fonction template amie, les arguments template par défaut sont autorisés uniquement si la déclaration est une définition, et aucune autre déclaration de cette fonction n'apparaît dans cette unité de traduction. |
(since C++11) |
Les arguments de template par défaut qui apparaissent dans les déclarations sont fusionnés de manière similaire aux arguments de fonction par défaut :
template<typename T1, typename T2 = int> class A; template<typename T1 = int, typename T2> class A; // ce qui précède est équivalent à ce qui suit : template<typename T1 = int, typename T2 = int> class A;
Mais le même paramètre ne peut pas recevoir d'arguments par défaut deux fois dans la même portée :
template<typename T = int> class X; template<typename T = int> class X {}; // erreur
Lors de l'analyse d'un argument de template par défaut pour un paramètre de template constant, le premier > non imbriqué est considéré comme la fin de la liste des paramètres du template plutôt qu'un opérateur supérieur à :
template<int i = 3 > 4> // erreur de syntaxe class X { /* ... */ }; template<int i = (3 > 4)> // OK class Y { /* ... */ };
Les listes de paramètres template des paramètres template template peuvent avoir leurs propres arguments par défaut, qui ne sont effectifs que là où le paramètre template template lui-même est dans la portée :
// modèle de classe, avec un paramètre de type template ayant une valeur par défaut template<typename T = float> struct B {}; // le paramètre template template T a une liste de paramètres, qui // consiste en un paramètre de type template avec une valeur par défaut template<template<typename = float> typename T> struct A { void f(); void g(); }; // définitions de fonctions membres template hors du corps template<template<typename TT> class T> void A<T>::f() { T<> t; // erreur : TT n'a pas de valeur par défaut dans la portée } template<template<typename TT = char> class T> void A<T>::g() { T<> t; // OK : t est T<char> }
Accès aux membres pour les noms utilisés dans un paramètre de template par défaut est vérifié lors de la déclaration, et non au point d'utilisation :
class B {}; template<typename T> class C { protected: typedef T TT; }; template<typename U, typename V = typename U::TT> class D: public U {}; D<C<B>>* d; // erreur : C::TT est protégé
|
L'argument template par défaut est implicitement instancié lorsque la valeur de cet argument par défaut est nécessaire, sauf si le template est utilisé pour nommer une fonction : template<typename T, typename U = int> struct S {}; S<bool>* p; // L'argument par défaut pour U est instancié à ce point // le type de p est S<bool, int>* |
(depuis C++14) |
Notes
Avant C++26, les paramètres de template constants étaient appelés paramètres de template non-type dans la terminologie standard. La terminologie a été modifiée par P2841R6 / PR#7587 .
|
Dans les paramètres de template, les contraintes de type peuvent être utilisées pour les paramètres de type et les paramètres constants, selon la présence de auto . template<typename> concept C = true; template<C, // type parameter C auto // constant parameter > struct S{}; S<int, 0> s;
|
(depuis C++20) |
| Macro de test de fonctionnalité | Valeur | Std | Fonctionnalité |
|---|---|---|---|
__cpp_nontype_template_parameter_auto
|
201606L
|
(C++17) | Déclaration des paramètres de template constants avec auto |
__cpp_nontype_template_args
|
201411L
|
(C++17) | Autoriser l'évaluation constante pour tous les arguments de template constants |
201911L
|
(C++20) | Types de classes et types à virgule flottante dans les paramètres de template constants |
Exemples
#include <array> #include <iostream> #include <numeric> // paramètre de template constant simple template<int N> struct S { int a[N]; }; template<const char*> struct S2 {}; // exemple constant complexe template < char c, // type intégral int (&ra)[5], // référence lvalue vers objet (de type tableau) int (*pf)(int), // pointeur vers fonction int (S<10>::*a)[10] // pointeur vers membre objet (de type int[10]) > struct Complicated { // appelle la fonction sélectionnée à la compilation // et stocke le résultat dans le tableau sélectionné à la compilation void foo(char base) { ra[4] = pf(c - base); } }; // S2<"fail"> s2; // erreur : littéral de chaîne ne peut être utilisé char okay[] = "okay"; // objet statique avec liaison // S2<&okay[0]> s3; // erreur : élément de tableau n'a pas de liaison S2<okay> s4; // fonctionne int a[5]; int f(int n) { return n; } // C++20 : NTTP peut être un type de classe littérale template<std::array arr> constexpr auto sum() { return std::accumulate(arr.cbegin(), arr.cend(), 0); } // C++20 : les arguments des templates de classe sont déduits au site d'appel static_assert(sum<std::array<double, 8>{3, 1, 4, 1, 5, 9, 2, 6}>() == 31.0); // C++20 : déduction d'argument NTTP et CTAD static_assert(sum<std::array{2, 7, 1, 8, 2, 8}>() == 28); int main() { S<10> s; // s.a est un tableau de 10 int s.a[9] = 4; Complicated<'2', a, f, &S<10>::a> c; c.foo('0'); std::cout << s.a[9] << a[4] << '\n'; }
Sortie :
42
|
Cette section est incomplète
Raison : plus d'exemples |
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 correct |
|---|---|---|---|
| CWG 184 | C++98 |
si les paramètres template des paramètres template template
sont autorisés à avoir des arguments par défaut n'est pas spécifié |
spécification ajoutée |
| CWG 1922 | C++98 |
il n'était pas clair si un template de classe dont le nom est un
nom de classe injecté peut utiliser les arguments par défaut dans les déclarations antérieures |
autorisé |
| CWG 2032 | C++14 |
pour les templates de variable, il n'y avait aucune restriction sur les paramètres
template après un paramètre template avec un argument par défaut |
appliquer la même restriction
que pour les templates de classe et les alias templates |
| CWG 2542 | C++20 | il n'était pas clair si le type de fermeture est structurel | il n'est pas structurel |
| CWG 2845 | C++20 | le type de fermeture n'était pas structurel |
il est structurel
s'il est sans capture |