Deducción de argumentos de plantilla
Para crear una instancia de una plantilla de función, se deben conocer todos los argumentos de la plantilla, pero no tienen que especificarse todos los argumentos de la plantilla. Cuando sea posible, el compilador deducirá los argumentos de plantilla que faltan de los argumentos de la función. Esto ocurre cuando se intenta una llamada a una función, cuando se toma una dirección de una plantilla de función y en algunos otros contextos:
template<typename To, typename From> To convert(From f); void g(double d) { int i = convert<int>(d); // llama a convert<int, double>(double) char c = convert<char>(d); // llama a convert<char, double>(double) int(*ptr)(float) = convert; // instancia a convert<int, float>(float) }
Este mecanismo hace posible utilizar operadores de plantilla, ya que no hay sintaxis para especificar argumentos de plantilla para un operador que no sea reescribiéndolo como una expresión de llamada a función:
La deducción argumentos de plantilla tiene lugar después de la búsqueda por nombre de la plantilla de función (que puede involucrar la búsqueda dependiente de argumento) y antes de la sustitución de argumentos de plantilla (que puede involucrar SFINAE) y la resolución de sobrecarga.
La deducción de argumentos de plantilla también se lleva a cabo cuando el nombre de una plantilla de clase se utiliza como el tipo de un objeto que está siendo construido: 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 deducción de argumentos de plantilla para plantillas de clase toma lugar en las declaraciones y en las expresiones de conversión explícitas; véase deducción de argumentos de plantillas de clase para más detalles. |
(desde C++17) |
Contenido |
[editar] Deducción desde una llamada a función
La deducción de argumentos de plantilla intenta determinar argumentos de plantilla (tipos para parámetros de plantilla de tipo T
i, plantillas para parámetros de plantilla de plantilla TT
i, y valores para parámetros de plantilla que no son de tipo I
i), que se pueden sustituir en cada parámetro P
para producir el tipo deducido A
, que es el mismo que el tipo del argumento A
, después de los ajustes que se enumeran a continuación.
Si hay varios parámetros, cada par P
/A
se deduce por separado y luego se combinan los argumentos de plantilla deducidos. Si la deducción falla o es ambigua para cualquier par P
/A
o si diferentes pares producen diferentes argumentos de plantilla deducidos, o si algún argumento de plantilla no se deduce ni se especifica explícitamente, la compilación falla.
Si al eliminar las referencias y los calificadores-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: se deduce T = int // P'2 = T, A'2 = 2: se deduce T = int // P'3 = T, A'3 = 3: se deduce T = int // correcto: se deduce T = int f({1, "abc"}); // P = std::initializer_list<T>, A = {1, "abc"} // P'1 = T, A'1 = 1: se deduce T = int // P'2 = T, A'2 = "abc": se deduce T = const char* // error: fallo de deducción, T es ambiguo Si al eliminar las referencias y calificadores-cv de template<class T, int N> void h(T const(&)[N]); h({1, 2, 3}); // se deduce T = int, y N = 3 template<class T> void j(T const(&)[3]); j({42}); // se deduce T = int, el límite del array no es una parámetro, no se considera struct Aggr { int i; int j; }; template<int N> void k(Aggr const(&)[N]); k({1, 2, 3}); // error: falla deducción, no hay conversión de int a Aggr k({{1}, {2}, {3}}); // correcto: se deduce N = 3 template<int M, int N> void m(int const(&)[M][N]); m({{1, 2}, {3, 4}}); // se deduce M = 2, y N = 2 template<class T, int N> void n(T const(&)[N], T); n({{1}, {2}, {3}}, Aggr()); // se deduce T = Aggr, y N = 3 Si un paquete de parámetros aparece como la última template<class... Types> void f(Types&...); void h(int x, float& y) { const int z = x; f(x, y, z); // P = Types&..., A1 = x: se deduce el primer miembro de Types... = int // P = Types&..., A2 = y: se deduce el segundo miembro de Types... = float // P = Types&..., A3 = z: se deduce el tercer miembro de Types... = const int // llama a f<int, float, const int> }
|
(desde C++11) |
Si P
es un tipo función, un puntero a un tipo función, o un puntero a un tipo función miembro y si A
es un conjunto de funciones sobrecargadas que no contienen plantillas de función, se intenta deducir el argumento de plantilla con cada sobrecarga. Si solo una tiene éxito, se utiliza esta. Si ninguna o más de una tiene éxito, el parámetro de plantilla es un contexto no deducido (véase más abajo):
template<class T> int f(T(*p)(T)); int g(int); int g(char); f(g); // P = T(*)(T), A = conjunto de sobrecargas // P = T(*)(T), A1 = int(int): se deduce T = int // P = T(*)(T), A2 = int(char): fallo al deducir T // solo una sobrecarga funciona, la deducción es correcta
Antes de que comience la deducción, se hacen los siguientes ajustes a P
y A
:
P
no es un tipo referencia, A
es un tipo array, A
se reemplaza por el puntero obtenido de la conversión array-a-puntero;A
es un tipo función, A
se reemplaza por el puntero obtenido de la conversión función-a-puntero;A
es un tipo calificado-cv, se ignoran los calificadores-cv de nivel superior para la deducción:
template<class T> void f(T); int a[3]; f(a); // P = T, A = int[3], se ajusta a int*: se deduce T = int* void b(int); f(b); // P = T, A = void(int), se ajusta a void(*)(int): se deduce T = void(*)(int) const int c = 13; f(c); // P = T, A = const int, se ajusta a int: se deduce T = int
P
es un tipo calificado-cv, los calificadores-cv de nivel superior se ignoran para la deducción.P
es un tipo referencia, se usa el tipo referenciado para la deducción.P
es una referencia rvalue a un parámetro de plantilla sin calificación-cv (llamada referencia avanzada), y el argumento de llamada de función correspondiente es un lvalue, el tipo de la referencia lvalue a A
se usa en lugar de A
para la deducción (Nota: esta es la base de acción de std::forward Nota: en la deducción de argumentos de plantilla de clase, el parámetro de plantilla de una plantilla de clase nunca es una referencia avanzada (desde C++17)):
template<class T> int f(T&&); // P es una referencia rvalue a T sin calificación-cv (referencia avanzada) template<class T> int g(const T&&); // P es una referencia rvalue a T calificada-cv (no especial) int main() { int i; int n1 = f(i); // el argumento es lvalue: llama a f<int&>(int&) (caso especial) int n2 = f(0); // el argumento no es lvalue: llama a f<int>(int&&) // int n3 = g(i); // error: se deduce g<int>(const int&&), que no se // puede vincular una referencia rvalue a un lvalue }
Después de estas transformaciones, la deducción procesa como se describe a continuación (comparar con la sección Deducción de un tipo) e intenta encontrar los argumentos de la plantilla que harían a la A
deducida (es decir, P
después de los ajustes enumerados anteriormente y la sustitución de los parámetros de plantilla deducidos) idéntica a la A
transformada, que es A
después de los ajustes enumerados anteriormente.
Si la deducción habitual de P
y A
falla, adicionalmente se consideran las siguientes alternativas:
P
es un tipo referencia, la A
deducida (es decir, el tipo al que hace referencia) puede estar más calificada-cv que la A
transformada:
template<typename T> void f(const T& t); bool a = false; f(a); // P = const T&, ajustada a const T, A = bool: // deducido T = bool, deducido A = const bool // A deducida es más calificada-cv que A
A
transformada puede ser otro puntero o puntero a tipo miembro que se puede convertir a la A
deducida a través de conversiones de calificación o una conversión de puntero a función (desde C++17):
template<typename T> void f(const T*); int* p; f(p); // P = const T*, A = int*: // T deducida = int, A deducida = const int* // se aplica conversión de calificación (de int* a const int*)
P
es una clase y P
tiene la forma de identificador-simple-de-plantilla, entonces la A
transformada puede ser una clase derivada de la A
deducida. Asimismo, si P
es un puntero a una clase de la forma identificador-simple-de-plantilla, la A
transformada puede ser un puntero a las clase derivada apuntada por la A
deducida:
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>&, se ajusta a P = B<T> (un identificador-simple-de-plantilla), A = D<int>: // T deducida = int, A deducida = B<int> // A es derivada de la A deducida }
[editar] Contextos no deducidos
En los siguientes casos, los tipos, plantillas y valores de no-tipo que se usan para componer P
no participan en la deducción de argumentos de plantilla, sino que usan los argumentos de plantilla que se dedujeron en otro lugar o especificados explícitamente. Si un parámetro de plantilla se usa solamente en contextos no deducidos y no se especifica explícitamente, la deducción del argumento de plantilla falla.
// la plantilla de identidad, a menudo utilizada para excluir argumentos específicos de la deducción // (disponible como std::type_identity a partir de C++20) template<typename T> struct identidad { typedef T tipo; }; template<typename T> void mal(std::vector<T> x, T valor = 1); template<typename T> void bien(std::vector<T> x, typename identidad<T>::tipo valor = 1); std::vector<std::complex<double>> x; mal(x, 1.2); // P1 = std::vector<T>, A1 = std::vector<std::complex<double>> // P1/A1: se deduce T = std::complex<double> // P2 = T, A2 = double // P2/A2: se deduce T = double // error: fallo deducción, T es ambiguo bien(x, 1.2); // P1 = std::vector<T>, A1 = std::vector<std::complex<double>> // P1/A1: se deduce T = std::complex<double> // P2 = identidad<T>::tipo, A2 = double // P2/A2: se usa T deducida por P1/A1 porque T está a la izquierda de :: en P2 // Correcto: T = std::complex<double>
2) La expresión de un especificador decltype:
template<typename T> void f(decltype(*std::declval<T>()) arg); int n; f<int*>(n); // P = decltype(*declval<T>()), A = int: T está en un contexto no deducido |
(desde 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á en un contexto no deducido, N no se puede deducir // nota: f(std::array<int, N> a) sería capaz de deducir 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 se deduce T = std::string // P2 = const F&, A2 = std::less<std::string> rvalue // P2 está en un contexto no deducido para F (parámetro de plantilla) usado en el // tipo parámetro (const F&) del parámetro de función comp, // que tiene un argumento predeterminado que se está usando en la llamada f(v)
P
, cuyo A
es una función o un conjunto de sobrecargas tal que más de una función coincide con P
o ninguna coincide con P
o el conjunto de sobrecarga incluye una o más plantillas de función:
P
, cuyo A
es una lista-de-inicialización-entre-paréntesis, pero P
no es std::initializer_list, una referencia a una (que puede estar calificada cv), o una referencia a un array (desde C++17):
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á en un contexto no deducido // error: T no está especificado explícitamente o deducido desde otro P/A g2({1, 2, 3}, 10); // P1 = std::vector<T>, A1 = {1, 2, 3}: T está en un contexto no deducido // P2 = T, A2 = int: se deduce T = int
7) El parámetro
P que es un paquete de parámetros y no aparece al final de la lista de parámetros:
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: se deduce T = int // P2 = Ts..., A2 = 2, A3 = 3, A4 = 4: se deduce Ts = [int, int, int] f2(1, 2, 3, 4); // P1 = Ts...: Ts es un contexto no deducido 8) La lista de parámetros de la plantilla que aparece dentro del parámetro P , y que incluye una expansión del paquete que no está al final de la lista de parámetros de plantilla:
template<int...> struct T {}; template<int... Ts1, int N, int... Ts2> void bien(const T<N, Ts1...>& arg1, const T<N, Ts2...>&); template<int... Ts1, int N, int... Ts2> void mal(const T<Ts1..., N>& arg1, const T<Ts2..., N>&); T<1, 2> t1; T<1, -1, 0> t2; bien(t1, t2); // P1 = const T<N, Ts1...>&, A1 = T<1, 2>: // se deduce N = 1, se deduce Ts1 = [2] // P2 = const T<N, Ts2...>&, A2 = T<1, -1, 0>: // se deduce N = 1, se deduce Ts2 = [-1, 0] mal(t1, t2); // P1 = const T<Ts1..., N>&, A1 = T<1, 2>: // <Ts1..., N> es un contexto no deducido // P2 = const T<Ts2..., N>&, A2 = T<1, -1, 0>: // <Ts2..., N> es un contexto no deducido |
(desde C++11) |
P
de tipo array (pero no referencia a array o puntero a array), el límite del array principal:
template<int i> void f1(int a[10][i]); template<int i> void f2(int a[i][20]); // P = int[i][20], tipo array template<int i> void f3(int (&a)[i][20]); // P = int(&)[i][20], referencia a un array void g() { int a[10][20]; f1(a); // correcto: se deduce i = 20 f1<20>(a); // correcto f2(a); // error: i es un contexto no deducido f2<10>(a); // correcto f3(a); // correcto: se deduce i = 10 f3<10>(a); // correcto }
En cualquier caso, si alguna parte del nombre de un tipo no se deduce, todo el nombre del tipo es un contexto no deducido. Sin embargo, los tipos compuesto pueden incluir nombres de tipos deducidos y no deducidos. Por ejemplo, en A<T>::B<T2>, T
es no deducido debido a la regla #1 (especificador de nombre anidado), y T2
es no deducido porque es parte del mismo nombre de tipo, pero en void(*f)(typename A<T>::B, A<T>), el T
en A<T>::B es no deducido (por la misma regla), mientras que T
en A<T> se deduce.
[editar] Deducción desde un tipo
Dado un parámetro de función P
que depende de uno o más parámetros de plantilla de tipo T
i, parámetros de plantilla de plantilla TT
i, o parámetros de plantilla de no tipo I
i, y el argumento correspondiente A
, la deducción tiene lugar si P
tiene una de las siguientes formas:
Esta sección está incompleta Razón: una tabla con micro ejemplos |
-
cv
(opcional)T
; -
T*
; -
T&
;
|
(desde C++11) |
-
T
(opcional)[
I
(opcional)]
;
|
(hasta C++17) |
|
(desde C++17) |
-
T
(opcional)U
(opcional)::*
; -
TT
(opcional)<T>
; -
TT
(opcional)<I>
; -
TT
(opcional)<TU>
; -
TT
(opcional)<>
.
En las formas anteriores,
-
T
(opcional) oU
(opcional) representan un tipo o lista de tipos de parámetros que satisfacen estas reglas recursivamente, es un contexto no deducido enP
oA
, o es el mismo tipo no dependiente enP
yA
. -
TT
(opcional) oTU
(opcional) representan una plantilla de clase o un parámetro de plantilla de plantilla. -
I
(opcional) representa una expresión que es unaI
, depende del valor enP
oA
, o tiene el mismo valor constante enP
yA
.
|
(desde C++17) |
Si P
tiene una de las formas que incluyen una lista de parámetros de plantilla <T>
o <I>
, entonces cada elemento P
i de esta lista de argumentos de plantilla se compara con el argumento de plantilla correspondiente A
i de su A
. Si el último P
i es una expansión de paquete, entonces su patrón se compara con cada argumento restante en la lista de argumentos de plantilla de A
. Un paquete de parámetros final que no se deduce de otra manera, se deduce como un paquete de parámetros vacío.
Si P
tiene una de las formas que incluyen una lista de parámetros de función (T)
, entonces cada parámetro P
i de esa lista se compara con el argumento correspondiente A
i de la lista de parámetros de función de A
. Si el último P
i es una expansión de paquete, entonces se compara con cada A
i restante en la lista de tipo de parámetros de A
.
Las formas se pueden anidar y procesar recursivamente:
- X<int>(*)(char[6]) es un ejemplo de
T*
, dondeT
es X<int>(char[6]);
|
(hasta C++17) |
|
(desde C++17) |
- X<int> es un ejemplo de
TT
(opcional)<T>
, dondeTT
esX
yT
es int, y - char[6] es un ejemplo de
T
(opcional)[
I
(opcional)]
, dondeT
es char eI
es std::size_t(6).
El argumento de plantilla de tipo no se puede deducir del tipo de un argumento de plantilla de no tipo: 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]: // se puede deducir i como igual a 20 // pero no se puede deducir T por el tipo de i |
(hasta C++17) |
Cuando el valor del argumento correspondiente a un parámetro de plantilla de no tipo P que se declara con un tipo dependiente se deduce de una expresión, los parámetros de plantilla en el tipo de P se deducen del tipo del valor. 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; // correcto: T se dedujo a long // a través del valor del argumento de plantilla en el tipo A<2> template<auto X> class bar{}; template<class T, T n> void f(bar<n> x); f(bar<3>{}); // OK: T se dedujo a int (y n a 3) // a través del valor del argumento de plantilla en el tipo bar<3> El tipo de template<class T, T i> void f(int (&a)[i]); int v[10]; f(v); // correcto: T es std::size_t El tipo 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; // correcto: X se deduce como true y el tipo de X se deduce como bool. |
(desde C++17) |
Si se usa un parámetro de plantilla de no tipo de plantilla de función en la lista de parámetros de plantilla de parámetros de función (que también es una plantilla), y se deduce el argumento de plantilla correspondiente, el tipo del argumento de plantilla deducido ( como se especifica en su lista de parámetros de plantilla adjunta, lo que significa que se conservan las referencias) debe coincidir exactamente con el tipo del parámetro de plantilla de no tipo, excepto que se eliminan los calificadores cv y excepto cuando el argumento de plantilla se deduce de un límite de array—en este caso se permite cualquier tipo entero, incluso bool, aunque siempre se convertiría en true:
template<int i> class A {}; template<short s> void f(A<s>); // el tipo del parámetro de plantilla de no tipo es short void k1() { A<1> a; // el tipo del parámetro de plantilla de no tipo de a es int f(a); // P = A<(short)s>, A = A<(int)1> // error: el argumento de plantilla de no tipo deducido no tiene // el mismo tipo que su correspondiente argumento de plantilla f<1>(a); // correcto: el argumento de plantilla no se deduce, // se llama a 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> // el tipo del parámetro es int& // el tipo del argumento es int& en la declaración de plantilla de struct X // correcto (con CWG 2091): se deduce R se refiere a n }
El parámetro de plantilla de tipo no se puede deducir del tipo de un argumento por defecto de función:
template<typename T> void f(T = 5, T = 7); void g() { f(1); // correcto: llama a f<int>(1, 7) f(); // error: no se deduce T f<int>(); // correcto: llama a f<int>(5, 7) }
La deducción del parámetro de plantilla de plantilla puede usar el tipo utilizado en la especialización de plantilla usada en la llamada de función:
template<template<typename> class X> struct A {}; // A es una plantilla con un parámetro 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>: se deduce TT = B, llama a f(A<B>)
[editar] Otros contextos
Además de las llamadas a funciones y las expresiones de operadores, la deducción de argumentos de plantilla se usa en las siguientes situaciones:
deducción de tipo autoLa deducción de argumentos de plantilla se usa en declaraciones de variables, cuando se deduce el significado del especificador auto del inicializador de la variable. El parámetro const auto& x = 1 + 2; // P = const U&, A = 1 + 2: // las mismas reglas que para llamar a f(1 + 2) donde si f es // template<class U> void f(const U& u) // se deduce U = int, el tipo de x es const int& auto l = {13}; // P = std::initializer_list<U>, A = {13}: // se deduce U = int, el tipo de l es std::initializer_list<int> En la inicialización de lista directa (pero no en la incialización de lista de copia), al deducir el significado de auto para una lista de incialización entre llaves, la lista de inicialización entre llaves debe contener solo un elemento, y el tipo de auto será el tipo de ese elmento: auto x1 = {3}; // x1 es std::initializer_list<int> auto x2{1, 2}; // error: no un solo elemento auto x3{3}; // x3 es int // (antes de N3922 x2 y x3 eran std::initializer_list<int>) |
(desde C++11) |
funciones que devuelven autoLa deducción de argumentos de plantilla se usa en declaraciones de funciones, cuando se deduce el significado del especificador auto en el tipo devuelto por la función, a partir de la sentencia de retorno. Para las funciones que devuelven auto, el parámetro auto f() { return 42; } // P = auto, A = 42: // se deduce U = int, el tipo de retorno de f es int Si dicha función tiene múltiples sentencias return, la deducción se realiza para cada sentencia return. Todos los tipos resultantes deben ser iguales y se convierte en el tipo devuelto. Si dicha función no tiene sentencia return, Nota: el significado del marcador de posición decltype(auto) en las declaraciones de variables y funciones no usa la deducción de argumentos de plantilla. |
(desde C++14) |
[editar] Resolución de sobrecarga
La deducción de argumentos de plantilla se usa durante la resolución de sobrecarga, al generar especializaciones a partir de una función de plantilla candidata.
P
y A
son lo mismo que en una llamada regular a función:
std::string s; std::getline(std::cin, s); // "std::getline" denomina a 4 plantillas de función, // 2 de las cuales son funciones candidatas (número correcto de parámetros) // 1ª plantilla candidata: // P1 = std::basic_istream<CharT, Traits>&, A1 = std::cin // P2 = std::basic_string<CharT, Traits, Allocator>&, A2 = s // la deducción determina el tipo de parámetros de plantilla CharT, Traits, y Allocator // especialización std::getline<char, std::char_traits<char>, std::allocator<char>> // 2ª plantilla candidata: // P1 = std::basic_istream<CharT, Traits>&&, A1 = std::cin // P2 = std::basic_string<CharT, Traits, Allocator>&, A2 = s // la deducción determina el tipo de parámetros de plantilla CharT, Traits, and Allocator // especialización std::getline<char, std::char_traits<char>, std::allocator<char>> // la resolución de sobrecarga clasifica el enlace de referencia lvalue std::cin // y elige la primera de las dos especializaciones candidatas
Si la deducción falla, o si tiene éxito, pero la especialización que produce no sería válida (por ejemplo, un operador sobrecargado cuyos parámetros no son tipos de clase ni de enumeración), la especialización no se incluye en el conjunto de sobrecarga, de forma similar a SFINAE.
[editar] Dirección de un conjunto de sobrecargas
La deducción de argumentos de plantilla se usa cuando se toma una dirección de un conjunto de sobrecargas, que incluye plantillas de funciones.
El tipo de función de la plantilla de función es P
. El tipo de destino es el tipo de A
:
std::cout << std::endl; // std::endl denomina una plantilla de función // tipo de endl P = // std::basic_ostream<CharT, Traits>& (std::basic_ostream<CharT, Traits>&) // operator<< parámetro A = // std::basic_ostream<char, std::char_traits<char>>& (*)( // std::basic_ostream<char, std::char_traits<char>>& // ) // (otras sobrecargas de operator<< no son viables) // la deducción determina el tipo de parámetros de plantilla CharT y Traits
Se aplica una regla adicional a la deducción en este caso: cuando se comparan parámetros de función P
i y A
i, si cualquier P
i es una referencia rvalue a un parámetro de plantilla sin calificación cv (una "referencia adelantada") y la correspondiente A
i es una referencia lvalue, entonces P
i se ajusta la tipo de parámetro de plantilla (T&& se hace T).
Si el tipo de retorno de la plantilla de función es una marcador de posición (auto or decltype(auto)), ese tipo de retorno es una contexto no deducido y se determina a partir de la creación de instancias. |
(desde C++14) |
[editar] Orden parcial
La deducción de argumentos de plantilla se usa durante la ordenación parcial de plantilla de función sobrecargadas
Esta sección está incompleta Razón: ejemplo |
[editar] Plantilla de función de conversión
Se usa la deducción de argumentos de plantilla cuando se seleccionan argumentos de plantilla de funciones de conversión definidas por usuario.
A
es el tipo requerido como resultado de la conversión. P
es el tipo de retorno de la plantilla de función de conversión. Si P
es un tipo referencia, entonces el tipo referido se usa en lugar de P
para las siguientes partes de la sección.
Si A
no es un tipo referencia:
P
es un tipo array, entonces el tipo puntero obtenido mediante la conversión puntero a array se usa en lugar de P
;P
es un tipo función, entonces el tipo puntero a función obtenido mediante la conversión de función a puntero se usa en lugar de P
;P
está calificada cv, los calificadores cv de nivel superior se ignoran.Si A
está calificada cv, se ignora los calificadores cv de nivel superior. Si A
es un tipo referencia, el tipo referenciado se usa por la deducción.
Si la deducción habitual de P
y A
(como se describe anteriormente) falla, adicionalmente se consideran las siguientes alternativas:
A
es un tipo referencia, A
puede ser más calificado cv que la A
deducida;A
es un puntero o puntero a tipo miembro, se permite que la A
deducida sea cualquier puntero que se pueda convertir a A
mediante una conversión de calificación:
struct A { template<class T> operator T***(); }; A a; const int* const* const* p1 = a; // P = T***, A = const int* const* const* // la deducción habitual de llamada a función para // template<class T> void f(T*** p) como si se llamara con el argumento // del tipo const int* const* const* falla // la deducción adicional para funciones de conversión determina T = int // (la A deducida es int***, convertible a const int* const* const*)
c) si
A es un tipo puntero a función, se permite que la A deducida sea un puntero a un función noexcept, convertible a A mediante la conversión de puntero a función;d) si
A es un puntero a función miembro, se permite que la A deducida sea un puntero a una función miembro noexcept, convertible a A mediante la conversión a puntero de función. |
(desde C++17) |
Vea plantilla de miembros para conocer otras reglas relacionadas con las plantillas de funciones de conversión.
[editar] Instanciación explícita
La deducción de argumentos de plantilla se usa en instanciaciones explícitas, especializaciones explícitas, y aquellas declaraciones friend en las que el id del declarador hace referencia a una especialización de una plantilla de función (por ejemplo, friend ostream& operator<< <> (...)), si no todos los argumentos de plantilla se especifican explícitamente o se establecen de forma predeterminada, se usa la deducción de argumentos de plantilla para determinar a qué especialización de plantilla se hace referencia.
P
es el tipo de plantilla de función que se considera para una posible coincidencia, y A
es el tipo función de la declaración. Si no hay coincidencias o hay más de una (después de la ordenación parcial), la declaración de la función está mal formada:
template<class X> void f(X a); // primera plantilla de f template<class X> void f(X* a); // segunda plantilla de f template<> void f<>(int* a) {} // especialización explícita de f // P1 = void(X), A1 = void(int*): se deduce X = int*, f<int*>(int*) // P2 = void(X*), A2 = void(int*): se deduce X = int, f<int>(int*) // f<int*>(int*) y f<int>(int*) se envían para ordenamiento parcial // que selecciona f<int>(int*) como la plantilla más especializada
En este caso se aplica una regla adicional a la deducción: cuando se comparan los parámetros de función P
i y A
i, si cualquier P
i es una referencia rvalue a un parámetro de plantilla sin calificación cv (una "referencia adelantada") y la correspondiente A
i es una referencia lvalue, entonces P
i se ajusta al tipo de parámetro de plantilla (T&& se hace T).
[editar] Plantilla de función de desasignación
La deducción de argumentos de plantilla se usa para determinar si una especialización de plantilla de función de desasignación coincide con una forma de ubicación del operator new
.
P
es el tipo de la plantilla de función que se está considerando como una coincidencia potencial, y A
es el tipo de función de la función de desasignación que sería la coincidencia para el operador de ubicación new en consideración. Si no hay ninguna coincidencia o hay más de una coincidencia (después de la resolución de sobrecarga), no se llama a la función de desasignación (puede ocurrir una perdida de memoria):
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; // cuando X() lanza excepción, se busca operator delete // P1 = void(void*, T), A1 = void(void*, bool): // se deduce T = bool // P2 = void(void*, T), A2 = void(void*, double): // se deduce T = double // la resolución de sobrecarga selecciona operator delete<bool> } catch(const std::exception&) {} try { X* p1 = new (13.2) X; // misma búsqueda, selecciona operator delete<double> } catch(const std::exception&) {} }
[editar] Plantillas de alias
Las plantillas de alias nunca se deducen, excepto en la deducción de argumentos de plantilla de clase (desde 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); // de acuerdo: se dedujo TT = vector template<template<class> class TT> void f(TT<int>); f(v); // ERROR: TT no puede deducirse como "Vec" porque Vec es una plantilla de alias
[editar] Conversiones implícitas
La deducción de tipo no tiene en cuenta las conversiones implícitas (aparte de los ajuste de tipos listados anteriormente): ese es trabajo para la resolución de sobrecarga, que ocurre después. Sin embargo, si la deducción tiene éxito para todos los parámetros que participan en la deducción de argumentos de plantilla, y todos los argumentos de plantilla que no se deducen se especifican explícitamente o se establecen por defecto, entonces los parámetros de función restantes se comparan con los argumentos de función correspondientes. Para cada parámetro P
restante con un tipo que no era dependiente antes de la sustitución de cualquier argumento de plantilla especificado explícitamente, si el argumento A
correspondiente no se puede convertir implícitamente en P
, la deducción falla.
Los parámetros con tipos dependientes en los que ningún parámetro de plantilla participe en la deducción de argumentos de plantilla y los parámetros que se volvieron no dependientes debido ala sustitución de argumentos de plantilla especificados explícitamente se verificarán durante la resolución de sobrecarga:
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); // para #1, la deducción determina T = struct A, pero el argumento restante 1 // no se puede convertir implícitamente a su parámetro void*: la deducción // falla // no se solicita la instanciación del tipo de retorno // para #2, la deducción determina T = struct A, y el argumento restante 1 // se puede convertir implícitamente a su parámetro int: la deducción tiene // éxito // la llamada a función compila como una llamada a #2 (el fallo de deducción // es SFINAE) }
[editar] Informes de defectos
Los siguientes informes de defectos de cambio de comportamiento se aplicaron de manera retroactiva a los estándares de C++ publicados anteriormente.
ID | Aplicado a | Comportamiento según lo publicado | Comportamiento correcto |
---|---|---|---|
CWG 70 | C++98 | no se especifico si los límites del array se deducen | especificado como no deducido |
CWG 300 | C++98 | la deducción tenía lugar para parámetros de función de la formatype(*)(T)/T(*)()/T(*)(T) , los puntero a funcióncoinciden con estas formas, pero las referencias a función no |
se cambia estas formas atype(T)/T()/T(T) para quepueda cubrir referencias |
CWG 322 | C++98 | los parámetros de tipo de los tipos de referencia no se ajustaron para usar el tipo de referencia para la deducción |
añadido ajuste |
CWG 976 | C++98 | en la deducción para plantilla de operador de conversión, el tipo de retorno const T& nunca coincidía con el tipo de retorno T
|
se ajustan las reglas para permitir dichas coincidencias |
CWG 1387 | C++11 | la expresión de un especificador decltype no era un contexto no deducido | lo es |
CWG 1391 | C++14 | El efecto de las conversiones implícitas de los argumentos que no estaban envueltos en la deducción no estaba especificado |
Se especificó como se describe anteriormente |
CWG 1591 | C++11 | No se puede deducir el límite de un array y el tipo de elemento de una lista de inicializadores entre llaves |
Se permite la deducción |
CWG 2052 | C++14 | Deducir un operador con argumentos no enumerados que no son de clase era un error grave |
Error ligero si existen otras sobrecargas |
CWG 2091 | C++98 | Deducir un parámetro de referencia de no tipo no funcionaba debido a la falta de coincidencia de tipo frente al argumento |
Se evita la falta de coincidencia del tipo |
N3922 | C++11 | La inicialización de lista directa de auto deduce std::initializer_list
|
Mal formado para más de un elemento, deduce el tipo de elemento para un solo elemento |
CWG 2355 | C++17 | un valor en un especificador noexcept de un tipo función no era deducible |
se hace deducible |