Namespaces
Variants

Template argument deduction

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

Para instanciar una function template , cada argumento de plantilla debe ser conocido, pero no todos los argumentos de plantilla tienen que ser especificados. Cuando es posible, el compilador deducirá los argumentos de plantilla faltantes a partir de los argumentos de la función. Esto ocurre cuando se intenta una llamada a función, cuando se toma la dirección de una function template, 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 convert<int, float>(float)
                                // y almacena su dirección en ptr
}

Este mecanismo hace posible utilizar operadores de plantilla, ya que no existe una sintaxis para especificar argumentos de plantilla para un operador que no sea reescribiéndolo como una expresión de llamada a función:

#include <iostream>
int main()
{
    std::cout << "Hola, mundo" << std::endl;
    // operator<< se busca mediante ADL como std::operator<<,
    // luego se deduce a operator<<<char, std::char_traits<char>> ambas veces
    // std::endl se deduce a &std::endl<char, std::char_traits<char>>
}

La deducción de argumentos de plantilla tiene lugar después de la búsqueda de nombre de la plantilla de función (que puede involucrar 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 realiza cuando el nombre de una plantilla de clase se utiliza como el tipo de un objeto que se está construyendo:

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 tiene lugar en declaraciones y en expresiones de conversión explícita; consulte deducción de argumentos de plantilla de clase para más detalles.

(desde C++17)

Contenidos

Deducción a partir de una llamada a función

La deducción de argumentos de plantilla intenta determinar los 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 constantes I i), que pueden ser sustituidos 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 enumerados a continuación.

Si hay múltiples parámetros, cada par P / A se deduce por separado y los argumentos de plantilla deducidos se combinan posteriormente. Si la deducción falla o es ambigua para cualquier par P / A , o si diferentes pares producen argumentos de plantilla deducidos distintos, o si algún argumento de plantilla permanece ni deducido ni explícitamente especificado, la compilación falla.

Si eliminar referencias y calificadores cv de P da como resultado std:: initializer_list < P '> y A es un braced-init-list , entonces se realiza la deducción para cada elemento de la lista de inicialización, tomando P' como parámetro y el elemento de la lista A' como argumento:

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: deduced T = int
               // P'2 = T, A'2 = 2: deduced T = int
               // P'3 = T, A'3 = 3: deduced T = int
               // OK: deduced T = int
f({1, "abc"}); // P = std::initializer_list<T>, A = {1, "abc"}
               // P'1 = T, A'1 = 1: deduced T = int
               // P'2 = T, A'2 = "abc": deduced T = const char*
               // error: deduction fails, T is ambiguous

Si eliminar referencias y calificadores cv de P da como resultado P' [ N ], y A es un braced-init-list no vacío, entonces se realiza la deducción como arriba, excepto que si N es un parámetro de plantilla constante, se deduce de la longitud de la lista de inicialización:

template<class T, int N>
void h(T const(&)[N]);
h({1, 2, 3}); // deduced T = int, deduced N = 3
template<class T>
void j(T const(&)[3]);
j({42}); // deduced T = int, array bound is not a parameter, not considered
struct Aggr
{
    int i;
    int j;
};
template<int N>
void k(Aggr const(&)[N]);
k({1, 2, 3});       // error: deduction fails, no conversion from int to Aggr
k({{1}, {2}, {3}}); // OK: deduced N = 3
template<int M, int N>
void m(int const(&)[M][N]);
m({{1, 2}, {3, 4}}); // deduced M = 2, deduced N = 2
template<class T, int N>
void n(T const(&)[N], T);
n({{1}, {2}, {3}}, Aggr()); // deduced T = Aggr, deduced N = 3

Si un parameter pack aparece como el último P , entonces el tipo P se compara con el tipo A de cada argumento restante de la llamada. Cada coincidencia deduce los argumentos de plantilla para la siguiente posición en la expansión del pack:

template<class... Types>
void f(Types&...);
void h(int x, float& y)
{
    const int z = x;
    f(x, y, z); // P = Types&..., A1 = x: deduced first member of Types... = int
                // P = Types&..., A2 = y: deduced second member of Types... = float
                // P = Types&..., A3 = z: deduced third member of Types... = const int
                // calls f<int, float, const int>
}


(desde C++11)

Si P es un tipo de función, puntero a tipo de función, o puntero a tipo de función miembro y si A es un conjunto de funciones sobrecargadas que no contiene plantillas de función, se intenta la deducción de argumentos de plantilla con cada sobrecarga. Si solo una tiene éxito, se utiliza esa deducción exitosa. Si ninguna o más de una tiene éxito, el parámetro de plantilla es contexto no deducido (ver 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): T deducido = int
      // P = T(*)(T), A2 = int(char): falla al deducir T
      // solo una sobrecarga funciona, la deducción tiene éxito

Antes de que comience la deducción, se realizan los siguientes ajustes a P y A :

1) Si P no es un tipo de referencia,
a) si A es un tipo array, A es reemplazado por el tipo puntero obtenido de la conversión array-a-puntero;
b) de lo contrario, si A es un tipo de función, A es reemplazado por el tipo puntero obtenido de la conversión función-a-puntero;
c) de lo contrario, si A es un tipo calificado con 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], adjusted to int*: deduced T = int*
void b(int);
f(b); // P = T, A = void(int), adjusted to void(*)(int): deduced T = void(*)(int)
const int c = 13;
f(c); // P = T, A = const int, adjusted to int: deduced T = int
2) Si P es un tipo calificado con cv, se ignoran los calificadores cv de nivel superior para la deducción.
3) Si P es un tipo de referencia, se utiliza el tipo referenciado para la deducción.
4) Si P es una referencia a valor-r a un parámetro de plantilla no calificado cv (las llamadas referencias de reenvío ), y el argumento correspondiente de la llamada a función es un lvalue, se utiliza el tipo referencia a lvalue de A en lugar de A para la deducción (Nota: esta es la base para la 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 de reenvío (desde C++17) ):
template<class T>
int f(T&&);       // P es una referencia a valor-r a T no calificado cv (referencia de reenvío)
template<class T>
int g(const T&&); // P es una referencia a valor-r a T calificado 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: deduce g<int>(const int&&), que
                   // no puede enlazar una referencia a valor-r a un lvalue
}

Después de estas transformaciones, el proceso de deducción procede como se describe a continuación (cf. sección deducción desde un tipo ) e intenta encontrar tales argumentos de plantilla que harían que el A deducido (es decir, P después de los ajustes enumerados anteriormente y la sustitución de los parámetros de plantilla deducidos) sea idéntico al transformado A , es decir A después de los ajustes enumerados anteriormente.

Si la deducción habitual de P y A falla, se consideran adicionalmente las siguientes alternativas:

1) Si P es un tipo de referencia, el A deducido (es decir, el tipo referenciado por la referencia) puede tener más calificadores cv que el A transformado:
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
2) El transformado A puede ser otro tipo de puntero o puntero a miembro que pueda convertirse al deducido A mediante una conversión 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*:
      // deduced T = int, deduced A = const int*
      // qualification conversion applies (from int* to const int*)
3) Si P es una clase y P tiene la forma simple-template-id , entonces el A transformado puede ser una clase derivada del A deducido. Del mismo modo, si P es un puntero a una clase de la forma simple-template-id , el A transformado puede ser un puntero a una clase derivada apuntado por el A deducido:
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>&, adjusted to P = B<T> (a simple-template-id), A = D<int>:
          // deduced T = int, deduced A = B<int>
          // A is derived from deduced A
}

Contextos no deducidos

En los siguientes casos, los tipos, plantillas y constantes que se utilizan para componer P no participan en la deducción de argumentos de plantilla, sino que utilizan los argumentos de plantilla que fueron deducidos en otra parte o especificados explícitamente. Si un parámetro de plantilla se utiliza solo en contextos no deducidos y no se especifica explícitamente, la deducción de argumentos de plantilla falla.

1) El nested-name-specifier (todo lo que está a la izquierda del operador de resolución de ámbito :: ) de un tipo que fue especificado usando un qualified-id :
// the identity template, often used to exclude specific arguments from deduction
// (available as std::type_identity as of 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: deduced T = std::complex<double>
              // P2 = T, A2 = double
              // P2/A2: deduced T = double
              // error: deduction fails, T is ambiguous
good(x, 1.2); // P1 = std::vector<T>, A1 = std::vector<std::complex<double>>
              // P1/A1: deduced T = std::complex<double>
              // P2 = identity<T>::type, A2 = double
              // P2/A2: uses T deduced by P1/A1 because T is to the left of :: in P2
              // OK: T = std::complex<double>
2) Un especificador de indexación de paquetes o una expresión de indexación de paquetes :
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
(desde C++26)
3) La expresión de un decltype -specifier:
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
(desde C++11)
4) Un argumento de plantilla constante o un límite de array en el cual una subexpresión hace referencia a un parámetro de plantilla:
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 es contexto no deducible, N no puede deducirse
      // nota: f(std::array<int, N> a) podría deducir N
5) Un parámetro de plantilla utilizado en el tipo de parámetro de un parámetro de función que tiene un argumento predeterminado que se está utilizando en la llamada para la cual se está realizando la deducción de argumentos:
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)
6) El parámetro 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 función coincide con P o el conjunto de sobrecargas incluye una o más plantillas de función:
template<typename T>
void out(const T& value) { std::cout << value; }
out("123");     // P = const T&, A = const char[4] lvalue: deduced T = char[4]
out(std::endl); // P = const T&, A = function template: T is in non-deduced context
7) El parámetro P , cuyo A es una lista de inicialización entre llaves, pero P no es std::initializer_list , una referencia a uno (posiblemente calificado con cv), o una referencia a un array}}:
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 contexto no deducido
                   // error: T no está especificado explícitamente o deducido de otro P/A
g2({1, 2, 3}, 10); // P1 = std::vector<T>, A1 = {1, 2, 3}: T está en contexto no deducido
                   // P2 = T, A2 = int: T deducido = int
8) 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: deducido T = int
                // P2 = Ts..., A2 = 2, A3 = 3, A4 = 4: deducido Ts = [int, int, int]
f2(1, 2, 3, 4); // P1 = Ts...: Ts es contexto no deducible
9) La lista de parámetros de plantilla que aparece dentro del parámetro P , y que incluye una expansión de 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 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>:
              // deducido N = 1, deducido Ts1 = [2]
              // P2 = const T<N, Ts2...>&, A2 = T<1, -1, 0>:
              // deducido N = 1, deducido Ts2 = [-1, 0]
bad(t1, t2);  // P1 = const T<Ts1..., N>&, A1 = T<1, 2>:
              // <Ts1..., N> es contexto no deducible
              // P2 = const T<Ts2..., N>&, A2 = T<1, -1, 0>:
              // <Ts2..., N> es contexto no deducible
(desde C++11)
10) Para P de tipo array (pero no referencia a array o puntero a array), el límite principal del array:
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: deduced i = 20
    f1<20>(a); // OK
    f2(a);     // error: i is non-deduced context
    f2<10>(a); // OK
    f3(a);     // OK: deduced i = 10
    f3<10>(a); // OK
}

En cualquier caso, si cualquier parte de un nombre de tipo es no deducida, todo el nombre de tipo es contexto no deducido. Sin embargo, los tipos compuestos pueden incluir tanto nombres de tipo deducidos como 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 (debido a la misma regla), mientras que el T en A < T > es deducido.

Deducción de 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 constantes I i, y el argumento correspondiente A , la deducción tiene lugar si P tiene una de las siguientes formas:

  • cv (opcional) T ;
  • T* ;
  • T& ;
  • T&& ;
(desde C++11)
  • T (opcional) [ I (opcional) ] ;
  • T (opcional) ( U (opcional) ) ;
(hasta C++17)
  • T (opcional) ( U (opcional) ) noexcept( I (opcional) ) ;
(desde C++17)
  • T (opcional) U (opcional) ::* ;
  • TT (opcional) <T> ;
  • TT (opcional) <I> ;
  • TT (opcional) <TU> ;
  • TT (opcional) <> .

En las formas anteriores,

  • T (opcional) o U (opcional) representa un tipo o parameter-type-list que cumple estas reglas recursivamente, es un contexto no deducido en P o A , o es el mismo tipo no dependiente en P y A .
  • TT (opcional) o TU (opcional) representa una plantilla de clase o un parámetro de plantilla de plantilla.
  • I (opcional) representa una expresión que es un I , es dependiente de valor en P o A , o tiene el mismo valor constante en P y A .
  • noexcept( I (opcional) ) representa una especificación de excepciones en la cual el operando posiblemente implícito del especificador noexcept satisface las reglas para un I (opcional) mencionado anteriormente.
(desde C++17)

Si P tiene una de las formas que incluye una lista de parámetros de plantilla <T> o <I> , entonces cada elemento P i de esa 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 incluye 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 su declarador se compara con cada A i restante en la lista de tipos de parámetros de A .

Los formularios pueden anidarse y procesarse de forma recursiva:

  • X < int > ( * ) ( char [ 6 ] ) es un ejemplo de T* , donde T es X < int > ( char [ 6 ] ) ;
  • X < int > ( char [ 6 ] ) es un ejemplo de T (opcional) ( U (opcional) ) , donde T es X < int > y U es char [ 6 ] ;
(hasta C++17)
  • X < int > ( char [ 6 ] ) es un ejemplo de T (opcional) ( U (opcional) ) noexcept( I (opcional) ) , donde T es X < int > , U es char [ 6 ] , y I en el especificador noexcept implícito es false ;
(desde C++17)
  • X < int > es un ejemplo de TT (opcional) <T> , donde TT es X y T es int , y
  • char [ 6 ] es un ejemplo de T (opcional) [ I (opcional) ] , donde T es char y I es std:: size_t ( 6 ) .

El argumento de plantilla de tipo no puede deducirse del tipo de un argumento de plantilla constante:

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 puede deducirse igual a 20
      // pero T no puede deducirse del tipo de i
(hasta C++17)

Cuando el valor del argumento correspondiente a un parámetro de plantilla constante 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; // OK: T se dedujo como long
                      // 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 como int (y n como 3)
             // del valor del argumento de plantilla en el tipo bar<3>

El tipo de N en el tipo T[N] es std::size_t .

template<class T, T i>
void f(int (&a)[i]);
int v[10];
f(v); // OK: T es std::size_t

El tipo de B en el especificador noexcept ( B ) de un tipo de función es bool .

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 se deduce como true y el tipo de X se deduce como bool.
(desde C++17)

Si un parámetro de plantilla constante de una plantilla de función se utiliza en la lista de parámetros de plantilla de un parámetro de función (que también es una plantilla), y el argumento de plantilla correspondiente se deduce, el tipo del argumento de plantilla deducido (como se especifica en su lista de parámetros de plantilla envolvente, lo que significa que las referencias se conservan) debe coincidir exactamente con el tipo del parámetro de plantilla constante, excepto que se eliminan los calificadores cv, y excepto cuando el argumento de plantilla se deduce de un límite de array—en ese caso se permite cualquier tipo integral, 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 constante es short
void k1()
{
    A<1> a;  // el tipo del parámetro de plantilla constante de a es int
    f(a);    // P = A<(short)s>, A = A<(int)1>
             // error: el argumento de plantilla constante deducido no tiene el mismo
             // tipo que su argumento de plantilla correspondiente
    f<1>(a); // OK: el argumento de plantilla no se deduce,
             // esto 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
           // OK (con CWG 2091): deduce que R hace referencia a n
}

El parámetro de plantilla de tipo no puede deducirse del tipo de un argumento predeterminado de función:

template<typename T>
void f(T = 5, T = 7);
void g()
{
    f(1);     // OK: llama a f<int>(1, 7)
    f();      // error: no se puede deducir T
    f<int>(); // OK: llama a f<int>(5, 7)
}

La deducción de parámetros de plantilla de plantilla puede utilizar el tipo utilizado en la especialización de plantilla utilizada en la llamada a la 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>: TT deducido = B, llama a f(A<B>)

Otros contextos

Además de las llamadas a funciones y las expresiones de operadores, la deducción de argumentos de plantilla se utiliza en las siguientes situaciones:

Deducción de tipo auto

La deducción de argumentos de plantilla se utiliza en declaraciones de variables, al deducir el significado del especificador auto a partir del inicializador de la variable.

El parámetro P se obtiene de la siguiente manera: en T , el tipo declarado de la variable que incluye auto , cada aparición de auto se reemplaza con un parámetro de plantilla de tipo imaginario U o, si la inicialización es de lista-copia, con std::initializer_list<U> . El argumento A es la expresión de inicialización. Después de deducir U a partir de P y A siguiendo las reglas descritas anteriormente, el U deducido se sustituye en P para obtener el tipo real de la variable:

const auto& x = 1 + 2; // P = const U&, A = 1 + 2:
                       // mismas reglas que para llamar f(1 + 2) donde f es
                       // template<class U> void f(const U& u)
                       // U deducido = int, el tipo de x es const int&
auto l = {13}; // P = std::initializer_list<U>, A = {13}:
               // U deducido = int, el tipo de l es std::initializer_list<int>

En la inicialización de lista directa (pero no en la inicialización de lista-copia), al deducir el significado de auto a partir de una lista de inicialización entre llaves, la lista debe contener solo un elemento, y el tipo de auto será el tipo de ese elemento:

auto x1 = {3}; // x1 es std::initializer_list<int>
auto x2{1, 2}; // error: no es un único elemento
auto x3{3};    // x3 es int
               // (antes de N3922 x2 y x3 eran ambos std::initializer_list<int>)
(desde C++11)

Funciones con retorno automático

La deducción de argumentos de plantilla se utiliza en declaraciones de funciones , al deducir el significado del especificador auto en el tipo de retorno de la función, a partir de la sentencia return.

Para funciones con retorno automático, el parámetro P se obtiene de la siguiente manera: en T , el tipo de retorno declarado de la función que incluye auto , cada aparición de auto se reemplaza con un parámetro de plantilla de tipo imaginario U . El argumento A es la expresión de la sentencia return , y si la sentencia return no tiene operando, A es void ( ) . Después de deducir U a partir de P y A siguiendo las reglas descritas anteriormente, el U deducido se sustituye en T para obtener el tipo de retorno real:

auto f() { return 42; } // P = auto, A = 42:
                        // deduced U = int, the return type of f is 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 convierten en el tipo de retorno real.

Si dicha función no tiene sentencia return, A es void ( ) al deducir.

Nota: el significado del marcador de posición decltype ( auto ) en declaraciones de variables y funciones no utiliza deducción de argumentos de plantilla.

(desde C++14)

Resolución de sobrecarga

La deducción de argumentos de plantilla se utiliza durante la resolución de sobrecarga , al generar especializaciones a partir de una función de plantilla candidata. P y A son los mismos que en una llamada de función regular:

std::string s;
std::getline(std::cin, s);
// "std::getline" nombra 4 plantillas de función,
// 2 de las cuales son funciones candidatas (número correcto de parámetros)
// Primera plantilla candidata:
// P1 = std::basic_istream<CharT, Traits>&, A1 = std::cin
// P2 = std::basic_string<CharT, Traits, Allocator>&, A2 = s
// la deducción determina los parámetros de plantilla de tipo CharT, Traits y Allocator
// especialización std::getline<char, std::char_traits<char>, std::allocator<char>>
// Segunda plantilla candidata:
// P1 = std::basic_istream<CharT, Traits>&&, A1 = std::cin
// P2 = std::basic_string<CharT, Traits, Allocator>&, A2 = s
// la deducción determina los parámetros de plantilla de tipo CharT, Traits y Allocator
// especialización std::getline<char, std::char_traits<char>, std::allocator<char>>
// la resolución de sobrecarga clasifica el enlace de referencia desde el lvalue std::cin
// y selecciona la primera de las dos especializaciones candidatas

Si la deducción falla, o si la deducción tiene éxito, pero la especialización que produce sería inválida (por ejemplo, un operador sobrecargado cuyos parámetros no son tipos de clase ni enumeración), la especialización no se incluye en el conjunto de sobrecarga, similar a SFINAE .

Dirección de un conjunto de sobrecarga

La deducción de argumentos de plantilla se utiliza al tomar la dirección de un conjunto sobrecargado , que incluye plantillas de función.

El tipo de función de la plantilla de función es P . El tipo destino es el tipo de A :

std::cout << std::endl;
// std::endl nombra una plantilla de función
// tipo de endl P =
// std::basic_ostream<CharT, Traits>& (std::basic_ostream<CharT, Traits>&)
// parámetro operator<< 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 los parámetros de plantilla de tipo CharT y Traits

Se aplica una regla adicional a la deducción en este caso: al comparar parámetros de función P i y A i, si cualquier P i es una referencia a valor r a parámetro de plantilla no calificado con cv (una "referencia de reenvío") y el correspondiente A i es una referencia a valor l, entonces P i se ajusta al tipo de parámetro de plantilla (T&& se convierte en T).

Si el tipo de retorno de la plantilla de función es un marcador de posición ( auto o decltype ( auto ) ), ese tipo de retorno es un contexto no deducido y se determina a partir de la instanciación.

(desde C++14)

Ordenamiento parcial

La deducción de argumentos de plantilla se utiliza durante el ordenamiento parcial de plantillas de función sobrecargadas .

Plantilla de función de conversión

La deducción de argumentos de plantilla se utiliza al seleccionar user-defined conversion function los argumentos de plantilla.

A es el tipo que se requiere 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 referenciado se utiliza en lugar de P para las siguientes partes de la sección.

Si A no es un tipo de referencia:

a) si el P es un tipo de arreglo, entonces el tipo de puntero obtenido mediante la conversión de arreglo a puntero se utiliza en lugar de P ;
b) si el P es un tipo de función, entonces el tipo de puntero a función obtenido mediante la conversión función-a-puntero se utiliza en lugar de P ;
c) si P está calificado con cv, se ignoran los calificadores cv de nivel superior.

Si A está calificado con cv, se ignoran los calificadores cv de nivel superior. Si A es un tipo de referencia, el tipo referenciado se utiliza para la deducción.

Si la deducción habitual de P y A (como se describe anteriormente) falla, se consideran adicionalmente las siguientes alternativas:

a) si A es un tipo de referencia, A puede estar más calificado con cv que el A deducido;
b) si A es un tipo puntero o puntero a miembro, se permite que el A deducido sea cualquier puntero que pueda convertirse a A mediante conversión de calificación:
struct C
{
    template<class T>
    operator T***();
};
C c;
const int* const* const* p1 = c;
// P = T***, A = const int* const* const*
// la deducción regular de llamada a función para
// template<class T> void f(T*** p) como si se llamara con el argumento
// de tipo const int* const* const* falla
// la deducción adicional para funciones de conversión determina T = int
// (el A deducido es int***, convertible a const int* const* const*)
c) si A es un tipo de puntero a función, el A deducido puede ser un puntero a función noexcept, convertible a A mediante conversión de puntero a función;
d) si A es un puntero a función miembro, el A deducido puede ser un puntero a función miembro noexcept, convertible a A mediante conversión de puntero a función.
(desde C++17)

Consulte member template para otras reglas relacionadas con las plantillas de función de conversión.

Instanciación explícita

La deducción de argumentos de plantilla se utiliza en instanciaciones explícitas , especializaciones explícitas , y aquellas declaraciones friend donde el declarator-id se refiere a una especialización de una plantilla de función (por ejemplo, friend ostream & operator << <> ( ... ) ), si no todos los argumentos de plantilla están explícitamente especificados o tienen valores por defecto, se utiliza la deducción de argumentos de plantilla para determinar a qué especialización de plantilla se hace referencia.

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 declaración. Si no hay coincidencias o hay más de una coincidencia (después del ordenamiento parcial), la declaración de función está mal formada:

template<class X>
void f(X a);        // 1ra plantilla f
template<class X>
void f(X* a);       // 2da plantilla f
template<>
void f<>(int* a) {} // especialización explícita de f
// P1 = void(X), A1 = void(int*): X deducido = int*, f<int*>(int*)
// P2 = void(X*), A2 = void(int*): X deducido = int, f<int>(int*)
// f<int*>(int*) y f<int>(int*) se someten luego a ordenamiento parcial
// que selecciona f<int>(int*) como la plantilla más especializada

Se aplica una regla adicional a la deducción en este caso: al comparar parámetros de función P i y A i, si cualquier P i es una referencia a valor r a parámetro de plantilla no calificado con cv (una "referencia de reenvío") y el correspondiente A i es una referencia a valor l, entonces P i se ajusta al tipo de parámetro de plantilla (T&& se convierte en T).

Plantilla de función de desasignación

La deducción de argumentos de plantilla se utiliza al determinar si una función de desasignación especialización de plantilla coincide con una forma de colocación dada de 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 new de colocación bajo consideración. Si no hay coincidencia o hay más de una coincidencia (después de la resolución de sobrecarga), la función de desasignación de colocación no se llama (puede ocurrir una fuga 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, se busca operator delete
                              // P1 = void(void*, T), A1 = void(void*, bool):
                              // T deducido = bool
                              // P2 = void(void*, T), A2 = void(void*, double):
                              // T deducido = 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&) {}
}

Plantillas de alias

Las plantillas de alias no se deducen , excepto en 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); // CORRECTO: TT deducido = 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

Conversiones implícitas

La deducción de tipos no considera conversiones implícitas (aparte de los ajustes de tipo listados anteriormente): esa es la tarea de la resolución de sobrecarga , que ocurre posteriormente. 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 están explícitamente especificados o tienen valores por defecto, entonces los parámetros de función restantes se comparan con los argumentos de función correspondientes. Para cada parámetro restante P con un tipo que no era dependiente antes de la sustitución de cualquier argumento de plantilla explícitamente especificado, si el argumento correspondiente A no puede convertirse implícitamente a P , la deducción falla.

Los parámetros con tipos dependientes en los que ningún parámetro de plantilla participa en la deducción de argumentos de plantilla, y los parámetros que se volvieron no dependientes debido a la sustitución de argumentos de plantilla explícitamente especificados serán verificados 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 puede convertirse implícitamente a su parámetro void*: la deducción falla
             // la instanciación del tipo de retorno no se solicita
             // para #2, la deducción determina T = struct A, y el argumento restante 1
             // puede convertirse implícitamente a su parámetro int: la deducción tiene éxito
             // la llamada a función se compila como una llamada a #2 (el fallo de deducción es SFINAE)
}

Informes de defectos

Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares publicados anteriormente de C++.

DR Aplicado a Comportamiento publicado Comportamiento correcto
CWG 70 C++98 no se especificaba si se deducirían los límites del array especificado como no deducible
CWG 300 C++98 la deducción ocurría para parámetros de función de la forma
type(*)(T)/T(*)()/T(*)(T) , los punteros a función
coinciden con estas formas pero las referencias a función no
cambiar estas formas a
type(T)/T()/T(T) para que
también puedan cubrir referencias
CWG 322 C++98 los parámetros de tipo de tipos referencia no eran
ajustados para usar el tipo referenciado en la deducción
se añadió el ajuste
CWG 976 C++98 en la deducción para plantillas de operadores de conversión,
const T& tipo de retorno nunca podía coincidir con T tipo resultado
reglas ajustadas para
permitir tales coincidencias
CWG 1387 C++11 la expresión de un especificador decltype no era un contexto no deducible lo es
CWG 1391 C++98 no se especificaba el efecto de conversiones implícitas de los argumentos
que no participaban en la deducción
especificado como se describe arriba
CWG 1591 C++11 no se puede deducir el límite del array y el tipo de elemento desde un braced-init-list deducción permitida
CWG 2052 C++98 deducir un operador con argumentos
no-clase no-enum era un error grave
error suave si hay
otras sobrecargas
CWG 2091 C++98 deducir un parámetro constante referencia no
funcionaba debido a incompatibilidad de tipo con el argumento
se evita la incompatibilidad de tipo
N3922 C++11 inicialización directa por lista de auto deduce std::initializer_list mal formado para más de un
elemento, deduce tipo de elemento
para elemento único
CWG 2355 C++17 el valor en un especificador noexcept de un tipo función no era deducible hecho deducible