Template argument deduction
Afin d'instancier une function template , chaque argument de template doit être connu, mais il n'est pas nécessaire que chaque argument de template soit spécifié. Lorsque cela est possible, le compilateur déduira les arguments de template manquants à partir des arguments de la fonction. Cela se produit lors d'une tentative d'appel de fonction, lors de la prise d'adresse d'une function template, et dans certains autres contextes :
template<typename To, typename From> To convert(From f); void g(double d) { int i = convert<int>(d); // appelle convert<int, double>(double) char c = convert<char>(d); // appelle convert<char, double>(double) int(*ptr)(float) = convert; // instancie convert<int, float>(float) // et stocke son adresse dans ptr }
Ce mécanisme permet d'utiliser des opérateurs template, puisqu'il n'existe pas de syntaxe pour spécifier des arguments template pour un opérateur autrement qu'en le réécrivant comme une expression d'appel de fonction :
La déduction des arguments de template a lieu après la recherche de nom du template de fonction (qui peut impliquer une recherche dépendante des arguments ) et avant la substitution des arguments de template (qui peut impliquer SFINAE ) et la résolution de surcharge .
|
La déduction d'arguments de modèle est également effectuée lorsque le nom d'un modèle de classe est utilisé comme type d'un objet en cours de construction : std::pair p(2, 4.5); std::tuple t(4, 3, 2.5); std::copy_n(vi1, 3, std::back_insert_iterator(vi2)); std::for_each(vi.begin(), vi.end(), Foo([&](int i) {...})); auto lck = std::lock_guard(foo.mtx); std::lock_guard lck2(foo.mtx, ul); La déduction d'arguments de modèle pour les modèles de classe a lieu dans les déclarations et dans les expressions de cast explicites ; voir déduction d'arguments de modèle de classe pour plus de détails. |
(depuis C++17) |
Table des matières |
Déduction à partir d'un appel de fonction
La déduction d'arguments de template tente de déterminer les arguments de template (types pour les paramètres de template de type
T
i, templates pour les paramètres de template de template
TT
i, et valeurs pour les paramètres de template constants
I
i), qui peuvent être substitués dans chaque paramètre
P
pour produire le type
déduit
A
, qui est le même que le type de l'argument
A
, après les ajustements listés ci-dessous.
S'il y a plusieurs paramètres, chaque paire
P
/
A
est déduite séparément et les arguments de modèle déduits sont ensuite combinés. Si la déduction échoue ou est ambiguë pour une paire
P
/
A
quelconque, ou si différentes paires produisent des arguments de modèle déduits différents, ou si un argument de modèle reste ni déduit ni explicitement spécifié, la compilation échoue.
|
Si la suppression des références et des qualificateurs cv de
template<class T> void f(std::initializer_list<T>); f({1, 2, 3}); // P = std::initializer_list<T>, A = {1, 2, 3} // P'1 = T, A'1 = 1: déduit T = int // P'2 = T, A'2 = 2: déduit T = int // P'3 = T, A'3 = 3: déduit T = int // OK: déduit T = int f({1, "abc"}); // P = std::initializer_list<T>, A = {1, "abc"} // P'1 = T, A'1 = 1: déduit T = int // P'2 = T, A'2 = "abc": déduit T = const char* // erreur: la déduction échoue, T est ambigu
Si la suppression des références et des qualificateurs cv de
template<class T, int N> void h(T const(&)[N]); h({1, 2, 3}); // déduit T = int, déduit N = 3 template<class T> void j(T const(&)[3]); j({42}); // déduit T = int, la borne du tableau n'est pas un paramètre, non considérée struct Aggr { int i; int j; }; template<int N> void k(Aggr const(&)[N]); k({1, 2, 3}); // erreur: la déduction échoue, pas de conversion de int vers Aggr k({{1}, {2}, {3}}); // OK: déduit N = 3 template<int M, int N> void m(int const(&)[M][N]); m({{1, 2}, {3, 4}}); // déduit M = 2, déduit N = 2 template<class T, int N> void n(T const(&)[N], T); n({{1}, {2}, {3}}, Aggr()); // déduit T = Aggr, déduit N = 3
Si un
pack de paramètres
apparaît comme le dernier
template<class... Types> void f(Types&...); void h(int x, float& y) { const int z = x; f(x, y, z); // P = Types&..., A1 = x: déduit premier membre de Types... = int // P = Types&..., A2 = y: déduit deuxième membre de Types... = float // P = Types&..., A3 = z: déduit troisième membre de Types... = const int // appelle f<int, float, const int> }
|
(depuis C++11) |
Si
P
est un type de fonction, un type de pointeur vers fonction, ou un type de pointeur vers fonction membre et si
A
est un
ensemble de fonctions surchargées
ne contenant pas de modèles de fonction, la déduction d'argument de modèle est tentée avec chaque surcharge. Si une seule réussit, cette déduction réussie est utilisée. Si aucune ou plus d'une réussit, le paramètre de modèle est un contexte non déduit (voir ci-dessous) :
template<class T> int f(T(*p)(T)); int g(int); int g(char); f(g); // P = T(*)(T), A = ensemble de surcharge // P = T(*)(T), A1 = int(int): T déduit = int // P = T(*)(T), A2 = int(char): échec de déduction de T // une seule surcharge fonctionne, la déduction réussit
Avant que la déduction ne commence, les ajustements suivants à
P
et
A
sont effectués :
P
n'est pas un type référence,
A
est un type tableau,
A
est remplacé par le type pointeur obtenu à partir de la conversion tableau-vers-pointeur ;
A
est un type de fonction,
A
est remplacé par le type pointeur obtenu à partir de la conversion fonction-vers-pointeur ;
A
est un type qualifié cv, les qualificatifs cv de plus haut niveau sont ignorés pour la déduction :
template<class T> void f(T); int a[3]; f(a); // P = T, A = int[3], ajusté en int* : T déduit = int* void b(int); f(b); // P = T, A = void(int), ajusté en void(*)(int) : T déduit = void(*)(int) const int c = 13; f(c); // P = T, A = const int, ajusté en int : T déduit = int
P
est un type qualifié cv, les qualificateurs cv de plus haut niveau sont ignorés pour la déduction.
P
est un type référence, le type référencé est utilisé pour la déduction.
P
est une référence à une valeur r vers un paramètre de modèle non qualifié cv (appelées
références de transfert
), et que l'argument correspondant de l'appel de fonction est une lvalue, le type référence lvalue vers
A
est utilisé à la place de
A
pour la déduction (Note : ceci est la base du fonctionnement de
std::forward
. Note : dans la
déduction d'arguments de modèle de classe
, le paramètre de modèle d'un modèle de classe n'est jamais une référence de transfert
(depuis C++17)
) :
template<class T> int f(T&&); // P est une référence à valeur r vers T non qualifié cv (référence de transfert) template<class T> int g(const T&&); // P est une référence à valeur r vers T qualifié cv (pas spécial) int main() { int i; int n1 = f(i); // l'argument est une lvalue : appelle f<int&>(int&) (cas spécial) int n2 = f(0); // l'argument n'est pas une lvalue : appelle f<int>(int&&) // int n3 = g(i); // erreur : déduit g<int>(const int&&), qui // ne peut pas lier une référence à valeur r à une lvalue }
Après ces transformations, le processus de déduction se déroule comme décrit ci-dessous (cf. section
déduction à partir d'un type
) et tente de trouver des arguments de template tels qu'ils rendraient le
A
déduit (c'est-à-dire,
P
après les ajustements listés ci-dessus et la substitution des paramètres de template déduits) identique au
transformé
A
, c'est-à-dire
A
après les ajustements listés ci-dessus.
Si la déduction habituelle à partir de
P
et
A
échoue, les alternatives suivantes sont également considérées :
P
est un type référence, le
A
déduit (c'est-à-dire le type référencé par la référence) peut être plus qualifié cv que le
A
transformé :
template<typename T> void f(const T& t); bool a = false; f(a); // P = const T&, adjusted to const T, A = bool: // deduced T = bool, deduced A = const bool // deduced A is more cv-qualified than A
A
transformé peut être un autre type de pointeur ou de pointeur vers membre qui peut être converti vers le
A
déduit via une
conversion de qualification
ou une conversion de pointeur de fonction
(depuis C++17)
:
template<typename T> void f(const T*); int* p; f(p); // P = const T*, A = int*: // T déduit = int, A déduit = const int* // la conversion de qualification s'applique (de int* vers const int*)
P
est une classe et
P
a la forme
simple-template-id
, alors le
A
transformé peut être une classe dérivée du
A
déduit. De même, si
P
est un pointeur vers une classe de la forme
simple-template-id
, le
A
transformé peut être un pointeur vers une classe dérivée pointée par le
A
déduit :
template<class T> struct B {}; template<class T> struct D : public B<T> {}; template<class T> void f(B<T>&) {} void f() { D<int> d; f(d); // P = B<T>&, ajusté à P = B<T> (un simple-template-id), A = D<int>: // T déduit = int, A déduit = B<int> // A est dérivé de A déduit }
Contextes non déduits
Dans les cas suivants, les types, modèles et constantes utilisés pour composer
P
ne participent pas à la déduction des arguments de modèle, mais utilisent plutôt
les arguments de modèle
qui ont été soit déduits ailleurs, soit explicitement spécifiés. Si un paramètre de modèle est utilisé uniquement dans des contextes non déduits et n'est pas explicitement spécifié, la déduction des arguments de modèle échoue.
// le modèle d'identité, souvent utilisé pour exclure des arguments spécifiques de la déduction // (disponible sous le nom std::type_identity à partir de C++20) template<typename T> struct identity { typedef T type; }; template<typename T> void bad(std::vector<T> x, T value = 1); template<typename T> void good(std::vector<T> x, typename identity<T>::type value = 1); std::vector<std::complex<double>> x; bad(x, 1.2); // P1 = std::vector<T>, A1 = std::vector<std::complex<double>> // P1/A1: T déduit = std::complex<double> // P2 = T, A2 = double // P2/A2: T déduit = double // erreur: la déduction échoue, T est ambigu good(x, 1.2); // P1 = std::vector<T>, A1 = std::vector<std::complex<double>> // P1/A1: T déduit = std::complex<double> // P2 = identity<T>::type, A2 = double // P2/A2: utilise T déduit par P1/A1 car T est à gauche de :: dans P2 // OK: T = std::complex<double>
|
2)
Un
spécificateur d'indexation de paquet
ou une
expression d'indexation de paquet
:
template<typename... Ts> void f(Ts...[0], std::tuple<Ts...>); f(3, std::tuple(5, 'A')); // P2 = std::tuple<Ts...>, A2 = std::tuple<int, char> // P2/A2: deduced first member of Ts... = int // P2/A2: deduced second member of Ts... = char // P1 = Ts...[0], A1 = int: Ts...[0] is in non-deduced context |
(depuis C++26) |
|
3)
L'expression d'un spécificateur
decltype
:
template<typename T> void f(decltype(*std::declval<T>()) arg); int n; f<int*>(n); // P = decltype(*declval<T>()), A = int: T is in non-deduced context |
(depuis C++11) |
template<std::size_t N> void f(std::array<int, 2 * N> a); std::array<int, 10> a; f(a); // P = std::array<int, 2 * N>, A = std::array<int, 10>: // 2 * N est un contexte non déduit, N ne peut pas être déduit // note : f(std::array<int, N> a) permettrait de déduire N
template<typename T, typename F> void f(const std::vector<T>& v, const F& comp = std::less<T>()); std::vector<std::string> v(3); f(v); // P1 = const std::vector<T>&, A1 = std::vector<std::string> lvalue // P1/A1 deduced T = std::string // P2 = const F&, A2 = std::less<std::string> rvalue // P2 is non-deduced context for F (template parameter) used in the // parameter type (const F&) of the function parameter comp, // that has a default argument that is being used in the call f(v)
P
, pour lequel
A
est une fonction ou un ensemble de surcharges tel que plus d'une fonction correspond à
P
ou aucune fonction ne correspond à
P
ou l'ensemble de surcharges inclut un ou plusieurs modèles de fonction :
P
, dont
A
est une liste d'initialisation entre accolades, mais
P
n'est pas
std::initializer_list
, une référence à celui-ci (éventuellement qualifiée cv), ou une référence à un tableau}} :
template<class T> void g1(std::vector<T>); template<class T> void g2(std::vector<T>, T x); g1({1, 2, 3}); // P = std::vector<T>, A = {1, 2, 3}: T est dans un contexte non déduit // erreur : T n'est pas explicitement spécifié ou déduit d'un autre P/A g2({1, 2, 3}, 10); // P1 = std::vector<T>, A1 = {1, 2, 3}: T est dans un contexte non déduit // P2 = T, A2 = int: T déduit = int
|
8)
Le paramètre
P
qui est un pack de paramètres et qui ne se trouve pas à la fin de la liste de paramètres :
template<class... Ts, class T> void f1(T n, Ts... args); template<class... Ts, class T> void f2(Ts... args, T n); f1(1, 2, 3, 4); // P1 = T, A1 = 1: déduit T = int // P2 = Ts..., A2 = 2, A3 = 3, A4 = 4: déduit Ts = [int, int, int] f2(1, 2, 3, 4); // P1 = Ts...: Ts est un contexte non déduit
9)
La liste de paramètres de template qui apparaît dans le paramètre
P
, et qui inclut une expansion de pack qui n'est pas à la toute fin de la liste de paramètres de template :
template<int...> struct T {}; template<int... Ts1, int N, int... Ts2> void good(const T<N, Ts1...>& arg1, const T<N, Ts2...>&); template<int... Ts1, int N, int... Ts2> void bad(const T<Ts1..., N>& arg1, const T<Ts2..., N>&); T<1, 2> t1; T<1, -1, 0> t2; good(t1, t2); // P1 = const T<N, Ts1...>&, A1 = T<1, 2>: // déduit N = 1, déduit Ts1 = [2] // P2 = const T<N, Ts2...>&, A2 = T<1, -1, 0>: // déduit N = 1, déduit Ts2 = [-1, 0] bad(t1, t2); // P1 = const T<Ts1..., N>&, A1 = T<1, 2>: // <Ts1..., N> est un contexte non déduit // P2 = const T<Ts2..., N>&, A2 = T<1, -1, 0>: // <Ts2..., N> est un contexte non déduit |
(depuis C++11) |
P
de type tableau (mais pas référence à un tableau ou pointeur vers un tableau), la dimension principale du tableau :
template<int i> void f1(int a[10][i]); template<int i> void f2(int a[i][20]); // P = int[i][20], array type template<int i> void f3(int (&a)[i][20]); // P = int(&)[i][20], reference to array void g() { int a[10][20]; f1(a); // OK: déduit i = 20 f1<20>(a); // OK f2(a); // erreur: i est un contexte non déduit f2<10>(a); // OK f3(a); // OK: déduit i = 10 f3<10>(a); // OK }
Dans tous les cas, si une partie d'un nom de type est non déduite, l'ensemble du nom de type est un contexte non déduit. Cependant, les types composés peuvent inclure à la fois des noms de types déduits et non déduits. Par exemple, dans
A
<
T
>
::
B
<
T2
>
,
T
est non déduit en raison de la règle n°1 (spécificateur de nom imbriqué), et
T2
est non déduit car il fait partie du même nom de type, mais dans
void
(
*
f
)
(
typename
A
<
T
>
::
B
, A
<
T
>
)
, le
T
dans
A
<
T
>
::
B
est non déduit (en raison de la même règle), tandis que le
T
dans
A
<
T
>
est déduit.
Déduction à partir d'un type
Étant donné un paramètre de fonction
P
qui dépend d'un ou plusieurs paramètres de template de type
T
i, de paramètres de template de template
TT
i, ou de paramètres de template constants
I
i, et l'argument correspondant
A
, la déduction a lieu si
P
a l'une des formes suivantes :
|
Cette section est incomplète
Raison : probablement un tableau avec des micro-exemples |
-
cv(optionnel)T; -
T*; -
T&;
|
(depuis C++11) |
-
T(optionnel)[I(optionnel)];
|
(jusqu'en C++17) |
|
(depuis C++17) |
-
T(optionnel)U(optionnel)::*; -
TT(optionnel)<T>; -
TT(optionnel)<I>; -
TT(optionnel)<TU>; -
TT(optionnel)<>.
Dans les formulaires ci-dessus,
-
T(optionnel) ouU(optionnel) représente un type ou parameter-type-list qui satisfait soit ces règles récursivement, soit est un contexte non déduit dansPouA, soit est le même type non dépendant dansPetA. -
TT(optionnel) ouTU(optionnel) représente soit un modèle de classe soit un paramètre de modèle de modèle. -
I(optionnel) représente une expression qui soit est unI, soit dépend des valeurs dansPouA, soit a la même valeur constante dansPetA.
|
(depuis C++17) |
Si
P
a l'une des formes qui inclut une liste de paramètres template
<T>
ou
<I>
, alors chaque élément
P
i de cette liste d'arguments template est comparé à l'argument template correspondant
A
i de son
A
. Si le dernier
P
i est une expansion de pack, alors son motif est comparé à chaque argument restant dans la liste d'arguments template de
A
. Un parameter pack final qui n'est pas autrement déduit est déduit comme un parameter pack vide.
Si
P
a l'une des formes qui incluent une liste de paramètres de fonction
(T)
, alors chaque paramètre
P
i de cette liste est comparé avec l'argument correspondant
A
i de la liste de paramètres de fonction de
A
. Si le dernier
P
i est une expansion de paquet, alors son déclarateur est comparé avec chaque
A
i restant dans la liste de types de paramètres de
A
.
Les formulaires peuvent être imbriqués et traités de manière récursive :
-
X
<
int
>
(
*
)
(
char
[
6
]
)
est un exemple de
T*, oùTest X < int > ( char [ 6 ] ) ;
|
(jusqu'en C++17) |
|
(depuis C++17) |
-
X
<
int
>
est un exemple de
TT(optionnel)<T>, oùTTestXetTest int , et -
char
[
6
]
est un exemple de
T(optionnel)[I(optionnel)], oùTest char etIest std:: size_t ( 6 ) .
|
L'argument de type template ne peut pas être déduit du type d'un argument template constant : template<typename T, T i> void f(double a[10][i]); double v[10][20]; f(v); // P = double[10][i], A = double[10][20]: // i peut être déduit comme égal à 20 // mais T ne peut pas être déduit du type de i |
(jusqu'en C++17) |
|
Lorsque la valeur de l'argument correspondant à un paramètre template constant P qui est déclaré avec un type dépendant est déduite d'une expression, les paramètres template dans le type de P sont déduits du type de la valeur. template<long n> struct A {}; template<class T> struct C; template<class T, T n> struct C<A<n>> { using Q = T; }; typedef long R; typedef C<A<2>>::Q R; // OK : T a été déduit comme long // à partir de la valeur d'argument template dans le type A<2> template<auto X> class bar {}; template<class T, T n> void f(bar<n> x); f(bar<3>{}); // OK : T a été déduit comme int (et n comme 3) // à partir de la valeur d'argument template dans le type bar<3>
Le type de
template<class T, T i> void f(int (&a)[i]); int v[10]; f(v); // OK : T est std::size_t
Le type de
template<bool> struct A {}; template<auto> struct B; template<auto X, void (*F)() noexcept(X)> struct B<F> { A<X> ax; }; void f_nothrow() noexcept; B<f_nothrow> bn; // OK : X est déduit comme true et le type de X est déduit comme bool. |
(depuis C++17) |
Si un paramètre de modèle constant d'un modèle de fonction est utilisé dans la liste des paramètres de modèle d'un paramètre de fonction (qui est également un modèle), et que l'argument de modèle correspondant est déduit, le type de l'argument de modèle déduit (tel que spécifié dans sa liste de paramètres de modèle englobante, ce qui signifie que les références sont préservées) doit correspondre exactement au type du paramètre de modèle constant, sauf que les qualificateurs cv sont supprimés, et sauf lorsque l'argument de modèle est déduit d'une limite de tableau—dans ce cas, tout type intégral est autorisé, même bool bien qu'il devienne toujours true :
template<int i> class A {}; template<short s> void f(A<s>); // le type du paramètre de template constant est short void k1() { A<1> a; // le type du paramètre de template constant de a est int f(a); // P = A<(short)s>, A = A<(int)1> // erreur : l'argument de template constant déduit n'a pas le même // type que son argument de template correspondant f<1>(a); // OK : l'argument de template n'est pas déduit, // ceci appelle f<(short)1>(A<(short)1>) } template<int&> struct X; template<int& R> void k2(X<R>&); int n; void g(X<n> &x) { k2(x); // P = X<R>, A = X<n> // le type du paramètre est int& // le type de l'argument est int& dans la déclaration du template de struct X // OK (avec CWG 2091) : déduit que R fait référence à n }
Le paramètre de modèle de type ne peut pas être déduit du type d'un argument par défaut de fonction :
template<typename T> void f(T = 5, T = 7); void g() { f(1); // OK : appelle f<int>(1, 7) f(); // erreur : impossible de déduire T f<int>(); // OK : appelle f<int>(5, 7) }
La déduction des paramètres template template peut utiliser le type utilisé dans la spécialisation de template utilisée dans l'appel de fonction :
template<template<typename> class X> struct A {}; // A est un template avec un paramètre TT template<template<typename> class TT> void f(A<TT>) {} template<class T> struct B {}; A<B> ab; f(ab); // P = A<TT>, A = A<B>: TT déduit = B, appelle f(A<B>)
Autres contextes
En plus des appels de fonction et des expressions d'opérateur, la déduction d'arguments de template est utilisée dans les situations suivantes :
Déduction automatique de typeLa déduction d'argument de template est utilisée dans les déclarations de variables, lors de la déduction de la signification du spécificateur auto à partir de l'initialiseur de la variable.
Le paramètre
const auto& x = 1 + 2; // P = const U&, A = 1 + 2: // mêmes règles que pour l'appel f(1 + 2) où f est // template<class U> void f(const U& u) // U déduit = int, le type de x est const int& auto l = {13}; // P = std::initializer_list<U>, A = {13}: // U déduit = int, le type de l est std::initializer_list<int> Dans l'initialisation directe par liste (mais pas dans l'initialisation de liste par copie), lors de la déduction de la signification du auto à partir d'une liste d'initialisation entre accolades, la liste d'initialisation entre accolades ne doit contenir qu'un seul élément, et le type de auto sera le type de cet élément : auto x1 = {3}; // x1 est std::initializer_list<int> auto x2{1, 2}; // erreur : pas un seul élément auto x3{3}; // x3 est int // (avant N3922 x2 et x3 étaient tous deux std::initializer_list<int>) |
(depuis C++11) |
Fonctions à retour autoLa déduction d'argument de template est utilisée dans les déclarations de fonctions , lors de la déduction de la signification du spécificateur auto dans le type de retour de la fonction, à partir de l'instruction return.
Pour les fonctions à retour auto, le paramètre
auto f() { return 42; } // P = auto, A = 42: // U déduit = int, le type de retour de f est int Si une telle fonction a plusieurs instructions return, la déduction est effectuée pour chaque instruction return. Tous les types résultants doivent être identiques et deviennent le type de retour réel.
Si une telle fonction n'a pas d'instruction return,
Remarque : la signification du spécificateur decltype ( auto ) dans les déclarations de variables et de fonctions n'utilise pas la déduction d'argument de template. |
(depuis C++14) |
Résolution de surcharge
La déduction d'argument de template est utilisée pendant
la résolution de surcharge
, lors de la génération de spécialisations à partir d'une fonction template candidate.
P
et
A
sont les mêmes que dans un appel de fonction régulier :
std::string s; std::getline(std::cin, s); // "std::getline" nomme 4 modèles de fonctions, // dont 2 sont des fonctions candidates (nombre correct de paramètres) // 1er modèle candidat : // P1 = std::basic_istream<CharT, Traits>&, A1 = std::cin // P2 = std::basic_string<CharT, Traits, Allocator>&, A2 = s // la déduction détermine les paramètres de type CharT, Traits et Allocator // spécialisation std::getline<char, std::char_traits<char>, std::allocator<char>> // 2ème modèle candidat : // P1 = std::basic_istream<CharT, Traits>&&, A1 = std::cin // P2 = std::basic_string<CharT, Traits, Allocator>&, A2 = s // la déduction détermine les paramètres de type CharT, Traits et Allocator // spécialisation std::getline<char, std::char_traits<char>, std::allocator<char>> // la résolution de surcharge classe la liaison de référence à partir de la lvalue std::cin // et sélectionne la première des deux spécialisations candidates
Si la déduction échoue, ou si la déduction réussit, mais que la spécialisation qu'elle produit serait invalide (par exemple, un opérateur surchargé dont les paramètres ne sont ni des types classe ni des types énumération), la spécialisation n'est pas incluse dans l'ensemble des surcharges, similaire à SFINAE .
Adresse d'un ensemble de surcharge
La déduction d'argument de template est utilisée lors de la prise d'une adresse d'un ensemble surchargé , qui inclut les function templates.
Le type de fonction du modèle de fonction est
P
. Le
type cible
est le type de
A
:
std::cout << std::endl; // std::endl désigne un modèle de fonction // type de endl P = // std::basic_ostream<CharT, Traits>& (std::basic_ostream<CharT, Traits>&) // paramètre A de operator<< = // std::basic_ostream<char, std::char_traits<char>>& (*)( // std::basic_ostream<char, std::char_traits<char>>& // ) // (les autres surcharges de operator<< ne sont pas viables) // la déduction détermine les paramètres de type template CharT et Traits
Une règle supplémentaire est appliquée à la déduction dans ce cas : lors de la comparaison des paramètres de fonction
P
i et
A
i, si un
P
i est une référence à valeur droite vers un paramètre de template non qualifié cv (une "forwarding reference") et le
A
i correspondant est une référence à valeur gauche, alors
P
i est ajusté au type du paramètre de template (T&& devient T).
|
Si le type de retour du modèle de fonction est un espace réservé ( auto ou decltype ( auto ) ), ce type de retour est un contexte non déduit et est déterminé à partir de l'instanciation. |
(depuis C++14) |
Ordonnancement partiel
La déduction des arguments de template est utilisée lors de la mise en ordre partielle des surcharges de templates de fonction .
|
Cette section est incomplète
Raison : mini-exemple |
Modèle de fonction de conversion
La déduction d'argument de template est utilisée lors de la sélection des arguments de template de fonction de conversion définie par l'utilisateur .
A
est le type requis comme résultat de la conversion.
P
est le type de retour du modèle de fonction de conversion. Si
P
est un type référence, alors le type référencé est utilisé à la place de
P
pour les parties suivantes de la section.
Si
A
n'est pas un type référence :
P
est un type tableau, alors le type pointeur obtenu par conversion tableau-vers-pointeur est utilisé à la place de
P
;
P
est un type de fonction, alors le type de pointeur de fonction obtenu par conversion fonction-vers-pointeur est utilisé à la place de
P
;
P
est qualifié cv, les qualificateurs cv de plus haut niveau sont ignorés.
Si
A
est qualifié cv, les qualificatifs cv de plus haut niveau sont ignorés. Si
A
est un type référence, le type référencé est utilisé pour la déduction.
Si la déduction habituelle à partir de
P
et
A
(comme décrit ci-dessus) échoue, les alternatives suivantes sont également considérées :
A
est un type référence,
A
peut être plus qualifié cv que le
A
déduit ;
A
est un type pointeur ou pointeur vers membre, le
A
déduit est autorisé à être n'importe quel pointeur convertible en
A
par conversion de qualification :
struct C { template<class T> operator T***(); }; C c; const int* const* const* p1 = c; // P = T***, A = const int* const* const* // déduction d'appel de fonction régulière pour // template<class T> void f(T*** p) comme si appelée avec l'argument // de type const int* const* const* échoue // déduction supplémentaire pour les fonctions de conversion détermine T = int // (A déduit est int***, convertible en const int* const* const*)
|
c)
si
A
est un type de pointeur de fonction, le
A
déduit peut être un pointeur vers une fonction noexcept, convertible en
A
par conversion de pointeur de fonction ;
d)
si
A
est un pointeur vers une fonction membre, le
A
déduit peut être un pointeur vers une fonction membre noexcept, convertible en
A
par conversion de pointeur de fonction.
|
(depuis C++17) |
Voir member template pour les autres règles concernant les modèles de fonction de conversion.
Instanciation explicite
La déduction d'arguments de template est utilisée dans les instanciations explicites , les spécialisations explicites , et ces déclarations friend où le declarator-id fait référence à une spécialisation d'un template de fonction (par exemple, friend ostream & operator << <> ( ... ) ), si tous les arguments de template ne sont pas explicitement spécifiés ou par défaut, la déduction d'arguments de template est utilisée pour déterminer à quelle spécialisation de template il est fait référence.
P
est le type du modèle de fonction qui est considéré comme une correspondance potentielle, et
A
est le type de fonction provenant de la déclaration. S'il n'y a aucune correspondance ou plus d'une correspondance (après classement partiel), la déclaration de fonction est mal formée :
template<class X> void f(X a); // 1er template f template<class X> void f(X* a); // 2ème template f template<> void f<>(int* a) {} // spécialisation explicite de f // P1 = void(X), A1 = void(int*): X déduit = int*, f<int*>(int*) // P2 = void(X*), A2 = void(int*): X déduit = int, f<int>(int*) // f<int*>(int*) et f<int>(int*) sont ensuite soumis à l'ordre partiel // qui sélectionne f<int>(int*) comme le template le plus spécialisé
Une règle supplémentaire s'applique à la déduction dans ce cas : lors de la comparaison des paramètres de fonction
P
i et
A
i, si un
P
i est une référence à valeur droite vers un paramètre de template non qualifié cv (une "forwarding reference") et le
A
i correspondant est une référence à valeur gauche, alors
P
i est ajusté au type du paramètre de template (T&& devient T).
Modèle de fonction de désallocation
La déduction d'argument de template est utilisée pour déterminer si une
fonction de désallocation
spécialisation de template correspond à une forme placement donnée de
operator new
.
P
est le type du modèle de fonction qui est considéré comme une correspondance potentielle, et
A
est le type de fonction de la fonction de désallocation qui serait la correspondance pour l'opérateur new de placement considéré. S'il n'y a pas de correspondance ou plus d'une correspondance (après résolution de surcharge), la fonction de désallocation de placement n'est pas appelée (une fuite de mémoire peut se produire) :
struct X { X() { throw std::runtime_error(""); } static void* operator new(std::size_t sz, bool b) { return ::operator new(sz); } static void* operator new(std::size_t sz, double f) { return ::operator new(sz); } template<typename T> static void operator delete(void* ptr, T arg) { ::operator delete(ptr); } }; int main() { try { X* p1 = new (true) X; // lorsque X() lance une exception, operator delete est recherché // P1 = void(void*, T), A1 = void(void*, bool) : // T déduit = bool // P2 = void(void*, T), A2 = void(void*, double) : // T déduit = double // la résolution de surcharge sélectionne operator delete<bool> } catch(const std::exception&) {} try { X* p1 = new (13.2) X; // même recherche, sélectionne operator delete<double> } catch(const std::exception&) {} }
Modèles d'alias
Les modèles d'alias ne sont pas déduits , sauf dans la déduction d'arguments de modèle de classe (depuis C++20) :
template<class T> struct Alloc {}; template<class T> using Vec = vector<T, Alloc<T>>; Vec<int> v; template<template<class, class> class TT> void g(TT<int, Alloc<int>>); g(v); // OK : déduit TT = vector template<template<class> class TT> void f(TT<int>); f(v); // erreur : TT ne peut pas être déduit comme "Vec" car Vec est un alias template
Conversions implicites
La déduction de type ne prend pas en compte les conversions implicites (autres que les ajustements de type listés ci-dessus) : c'est le rôle de la
résolution de surcharge
, qui intervient ultérieurement. Cependant, si la déduction réussit pour tous les paramètres participant à la déduction d'arguments de template, et que tous les arguments de template non déduits sont explicitement spécifiés ou par défaut, alors les paramètres de fonction restants sont comparés avec les arguments de fonction correspondants. Pour chaque paramètre restant
P
dont le type était non dépendant avant la substitution de tout argument de template explicitement spécifié, si l'argument correspondant
A
ne peut pas être implicitement converti en
P
, la déduction échoue.
Les paramètres avec des types dépendants dans lesquels aucun paramètre de template ne participe à la déduction d'arguments de template, et les paramètres devenus non-dépendants en raison de la substitution d'arguments de template explicitement spécifiés seront vérifiés lors de la résolution de surcharge :
template<class T> struct Z { typedef typename T::x xx; }; template<class T> typename Z<T>::xx f(void*, T); // #1 template<class T> void f(int, T); // #2 struct A {} a; int main() { f(1, a); // pour #1, la déduction détermine T = struct A, mais l'argument restant 1 // ne peut pas être implicitement converti en son paramètre void* : la déduction échoue // l'instanciation du type de retour n'est pas demandée // pour #2, la déduction détermine T = struct A, et l'argument restant 1 // peut être implicitement converti en son paramètre int : la déduction réussit // l'appel de fonction compile comme un appel à #2 (l'échec de déduction est SFINAE) }
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 | Applicable à | Comportement publié | Comportement corrigé |
|---|---|---|---|
| CWG 70 | C++98 | la déduction des limites de tableau n'était pas spécifiée | spécifiée comme non déduite |
| CWG 300 | C++98 |
la déduction avait lieu pour les paramètres de fonction de forme
type(*)(T)/T(*)()/T(*)(T)
, les pointeurs de fonction
correspondent à ces formes mais pas les références de fonction |
modification de ces formes en
type(T)/T()/T(T)
pour qu'elles
puissent également couvrir les références |
| CWG 322 | C++98 |
les paramètres de type références n'étaient pas
ajustés pour utiliser le type référencé lors de la déduction |
ajustement ajouté |
| CWG 976 | C++98 |
dans la déduction pour les modèles d'opérateur de conversion,
const T&
comme type de retour ne pouvait jamais correspondre
au type de résultat
T
|
règles ajustées pour
permettre de telles correspondances |
| CWG 1387 | C++11 | l'expression d'un spécificateur decltype n'était pas un contexte non déduit | elle l'est désormais |
| CWG 1391 | C++98 |
l'effet des conversions implicites des arguments
non impliqués dans la déduction n'était pas spécifié |
spécifié comme décrit ci-dessus |
| CWG 1591 | C++11 |
impossible de déduire la limite de tableau et le type d'élément
à partir d'un braced-init-list |
déduction autorisée |
| CWG 2052 | C++98 |
déduire un opérateur avec des arguments non-classe
non-énumération était une erreur fatale |
erreur non fatale s'il
existe d'autres surcharges |
| CWG 2091 | C++98 |
la déduction d'un paramètre constant référence
ne fonctionnait pas à cause d'une incompatibilité de type avec l'argument |
incompatibilité de type évitée |
| N3922 | C++11 | l'initialisation par liste directe de auto déduit std::initializer_list |
non valide pour plus d'un
élément, déduction du type d'élément pour un seul élément |
| CWG 2355 | C++17 | la valeur dans un spécificateur noexcept d'un type de fonction n'était pas déductible | rendue déductible |