Namespaces
Variants

Function template

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
Class template
Function template
Miscellaneous

Una plantilla de función define una familia de funciones.

Contenidos

Sintaxis

template < lista-de-parámetros > declaración-de-función (1)
template < lista-de-parámetros > requires restricción declaración-de-función (2) (desde C++20)
declaración-de-función-con-marcadores-de-posición (3) (desde C++20)
export template < lista-de-parámetros > declaración-de-función (4) (eliminado en C++11)

Explicación

parameter-list - una lista no vacía separada por comas de los parámetros de plantilla , cada uno de los cuales es un parámetro constante , un parámetro de tipo , un parámetro de plantilla , o un paquete de parámetros de cualquiera de estos (desde C++11) . Como con cualquier plantilla, los parámetros pueden estar restringidos (desde C++20)
function-declaration - una declaración de función . El nombre de función declarado se convierte en un nombre de plantilla.
constraint - una expresión de restricción que restringe los parámetros de plantilla aceptados por esta plantilla de función
function-declaration-
with-placeholders
- una declaración de función donde el tipo de al menos un parámetro utiliza el marcador de posición auto o Concept auto : la lista de parámetros de plantilla tendrá un parámetro inventado para cada marcador de posición (ver Plantillas de función abreviadas abajo)

export era un modificador opcional que declaraba la plantilla como exportada (cuando se usaba con una plantilla de clase, declaraba todos sus miembros como exportados también). Los archivos que instanciaban plantillas exportadas no necesitaban incluir sus definiciones: la declaración era suficiente. Las implementaciones de export eran raras y discrepaban entre sí en los detalles.

(hasta C++11)

Plantilla de función abreviada

Cuando los tipos de marcador de posición (ya sea auto o Concept auto ) aparecen en la lista de parámetros de una declaración de función o de una declaración de plantilla de función, la declaración declara una plantilla de función, y se añade un parámetro de plantilla inventado por cada marcador de posición a la lista de parámetros de plantilla:

void f1(auto); // same as template<class T> void f1(T)
void f2(C1 auto); // same as template<C1 T> void f2(T), if C1 is a concept
void f3(C2 auto...); // same as template<C2... Ts> void f3(Ts...), if C2 is a concept
void f4(const C3 auto*, C4 auto&); // same as template<C3 T, C4 U> void f4(const T*, U&);
template<class T, C U>
void g(T x, U y, C auto z); // same as template<class T, C U, C W> void g(T x, U y, W z);

Las plantillas de función abreviadas pueden especializarse como todas las plantillas de función.

template<>
void f4<int>(const int*, const double&); // specialization of f4<int, const double>


(desde C++20)

Firma de plantilla de función

Cada plantilla de función tiene una firma.

La firma de una template-head es la lista de parámetros de plantilla , excluyendo los nombres de los parámetros de plantilla y los argumentos predeterminados , y la cláusula requires (si existe) (desde C++20) .

La firma de una plantilla de función contiene el nombre, la lista de tipos de parámetros, el tipo de retorno , cláusula requires final (si existe) (desde C++20) , y la firma del template-head . Excepto en los siguientes casos, su firma también contiene el espacio de nombres contenedor.

Si la plantilla de función es un miembro de clase, su firma contiene la clase de la cual la función es miembro en lugar del espacio de nombres envolvente. Su firma también contiene la cláusula requires final (si existe) (desde C++20) , calificador de referencia (si existe), y (desde C++11) cv -calificadores (si existen).

Si la plantilla de función es un friend con restricción que involucra parámetros de plantilla de la clase envolvente, su firma contiene la clase envolvente en lugar del espacio de nombres envolvente.

(since C++20)

Instanciación de plantilla de función

Una plantilla de función por sí misma no es un tipo, ni una función. No se genera código a partir de un archivo fuente que contiene solo definiciones de plantillas. Para que aparezca cualquier código, una plantilla debe ser instanciada: los argumentos de la plantilla deben determinarse para que el compilador pueda generar una función real (o clase, a partir de una plantilla de clase).

Instanciación explícita

template tipo-de-retorno nombre < lista-de-argumentos > ( lista-de-parámetros ) ; (1)
template tipo-de-retorno nombre ( lista-de-parámetros ) ; (2)
extern template tipo-de-retorno nombre < lista-de-argumentos > ( lista-de-parámetros ) ; (3) (desde C++11)
extern template tipo-de-retorno nombre ( lista-de-parámetros ) ; (4) (desde C++11)
1) Definición de instanciación explícita (sin deducción de argumentos de plantilla si cada parámetro de plantilla no predeterminado está especificado explícitamente)
2) Definición de instanciación explícita con deducción de argumentos de plantilla para todos los parámetros
3) Declaración de instanciación explícita (sin deducción de argumentos de plantilla si cada parámetro de plantilla no predeterminado está especificado explícitamente)
4) Declaración de instanciación explícita con deducción de argumentos de plantilla para todos los parámetros

Una definición de instanciación explícita fuerza la instanciación de la función o función miembro a la que se refiere. Puede aparecer en cualquier parte del programa después de la definición de la plantilla, y para una lista de argumentos dada, solo se permite que aparezca una vez en el programa, sin requerirse diagnóstico.

Una declaración de instanciación explícita (una plantilla externa) evita las instanciaciones implícitas: el código que de otra manera causaría una instanciación implícita debe usar la definición de instanciación explícita proporcionada en otra parte del programa.

(since C++11)

Un argumento de plantilla final puede dejarse sin especificar en una instanciación explícita de una especialización de plantilla de función o de una especialización de plantilla de función miembro si puede ser deducido a partir del parámetro de función:

template<typename T>
void f(T s)
{
    std::cout << s << '\n';
}
template void f<double>(double); // instancia f<double>(double)
template void f<>(char);         // instancia f<char>(char), argumento de plantilla deducido
template void f(int);            // instancia f<int>(int), argumento de plantilla deducido

La instanciación explícita de una plantilla de función o de una función miembro de una plantilla de clase no puede utilizar inline o constexpr . Si la declaración de la instanciación explícita nombra una función miembro especial declarada implícitamente, el programa está mal formado.

La instanciación explícita de un constructor no puede usar una lista de parámetros de plantilla (sintaxis (1) ), lo cual tampoco es necesario nunca porque pueden ser deducidos (sintaxis (2) ).

La instanciación explícita de un destructor prospectivo debe nombrar el destructor seleccionado de la clase.

(since C++20)

Las declaraciones de instanciación explícita no suprimen la instanciación implícita de inline functions, auto -declarations, references, and class template specializations. (por lo tanto, cuando la función inline que es objeto de declaración de instanciación explícita es ODR-used, se instancia implícitamente para inlining, pero su copia out-of-line no se genera en esta unidad de traducción)

La definición de instanciación explícita de una plantilla de función con argumentos predeterminados no es un uso de los argumentos, y no intenta inicializarlos:

char* p = 0;
template<class T>
T g(T x = &p) { return x; }
template int g<int>(int); // Correcto aunque &p no sea un int.

Instanciación implícita

Cuando el código hace referencia a una función en un contexto que requiere que exista la definición de la función , o si la existencia de la definición afecta la semántica del programa (desde C++11) , y esta función particular no ha sido instanciada explícitamente, ocurre la instanciación implícita. La lista de argumentos de plantilla no tiene que ser proporcionada si puede ser deducida del contexto.

#include <iostream>
template<typename T>
void f(T s)
{
    std::cout << s << '\n';
}
int main()
{
    f<double>(1); // instancia y llama a f<double>(double)
    f<>('a');     // instancia y llama a f<char>(char)
    f(7);         // instancia y llama a f<int>(int)
    void (*pf)(std::string) = f; // instancia f<string>(string)
    pf("∇");                     // llama a f<string>(string)
}

Se considera que la existencia de una definición de función afecta a la semántica del programa si la función es necesaria para la evaluación constante por una expresión, incluso si la evaluación constante de la expresión no es requerida o si la evaluación de expresión constante no utiliza la definición.

template<typename T>
constexpr int f() { return T::value; }
template<bool B, typename T>
void g(decltype(B ? f<T>() : 0));
template<bool B, typename T>
void g(...);
template<bool B, typename T>
void h(decltype(int{B ? f<T>() : 0}));
template<bool B, typename T>
void h(...);
void x()
{
    g<false, int>(0); // OK: B ? f<T>() : 0 is not potentially constant evaluated
    h<false, int>(0); // error: instantiates f<int> even though B evaluates to false
                      // and list-initialization of int from int cannot be narrowing
}
(desde C++11)

Nota: omitir <> completamente permite que la resolución de sobrecarga examine tanto las sobrecargas de plantilla como las no plantilla.

Deducción de argumentos de plantilla

Para instanciar una plantilla de función, 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 y cuando se toma la dirección de una plantilla de función.

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)
}

Este mecanismo hace posible utilizar operadores de plantilla, ya que no existe una sintaxis para especificar argumentos de plantilla para un operador, excepto 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 resolución de sobrecarga .

Consulte deducción de argumentos de plantilla para más detalles.

Argumentos de plantilla explícitos

Los argumentos de plantilla de una plantilla de función pueden obtenerse de

  • deducción de argumentos de plantilla
  • argumentos de plantilla predeterminados
  • especificados explícitamente, lo cual puede hacerse en los siguientes contextos:
  • en una expresión de llamada a función
  • cuando se toma la dirección de una función
  • cuando se inicializa una referencia a función
  • cuando se forma un puntero a función miembro
  • en una especialización explícita
  • en una instanciación explícita
  • en una declaración friend

No hay forma de especificar explícitamente argumentos de plantilla para operadores sobrecargados , funciones de conversión , y constructores, porque se llaman sin usar el nombre de la función.

Los argumentos de plantilla especificados deben coincidir con los parámetros de plantilla en tipo (es decir, tipo por tipo, constante por constante, y plantilla por plantilla). No puede haber más argumentos que parámetros (a menos que un parámetro sea un paquete de parámetros, en cuyo caso debe haber un argumento para cada parámetro no-pack) (desde C++11) .

Los argumentos constantes especificados deben coincidir con los tipos de los correspondientes parámetros de plantilla constantes, o ser convertibles a ellos .

Los parámetros de función que no participan en la deducción de argumentos de plantilla (por ejemplo, si los argumentos de plantilla correspondientes están explícitamente especificados) están sujetos a conversiones implícitas al tipo del parámetro de función correspondiente (como en la resolución de sobrecarga habitual overload resolution ).

Un paquete de parámetros de plantilla que se especifica explícitamente puede extenderse mediante deducción de argumentos de plantilla si hay argumentos adicionales:

template<class... Types>
void f(Types... values);
void g()
{
    f<int*, float*>(0, 0, 0); // Types = {int*, float*, int}
}
(desde C++11)

Sustitución de argumentos de plantilla

Cuando todos los argumentos de plantilla han sido especificados, deducidos u obtenidos de los argumentos de plantilla predeterminados, cada uso de un parámetro de plantilla en la lista de parámetros de la función es reemplazado con los argumentos de plantilla correspondientes.

La falla de sustitución (es decir, la incapacidad de reemplazar los parámetros de plantilla con los argumentos de plantilla deducidos o proporcionados) de una plantilla de función elimina la plantilla de función del conjunto de sobrecarga . Esto permite varias formas de manipular conjuntos de sobrecarga usando metaprogramación de plantillas: consulte SFINAE para más detalles.

Después de la sustitución, todos los parámetros de función de tipo array y función se ajustan a punteros y todos los calificadores cv de nivel superior se eliminan de los parámetros de función (como en una declaración de función regular).

La eliminación de los calificadores cv de nivel superior no afecta el tipo del parámetro tal como aparece dentro de la función:

template<class T>
void f(T t);
template<class X>
void g(const X x);
template<class Z>
void h(Z z, Z* zp);
// dos funciones diferentes con el mismo tipo, pero 
// dentro de la función, t tiene diferentes calificaciones cv
f<int>(1);       // el tipo de función es void(int), t es int
f<const int>(1); // el tipo de función es void(int), t es const int
// dos funciones diferentes con el mismo tipo y la misma x
// (los punteros a estas dos funciones no son iguales,
//  y los estáticos locales de función tendrían direcciones diferentes)
g<int>(1);       // el tipo de función es void(int), x es const int
g<const int>(1); // el tipo de función es void(int), x es const int
// solo se eliminan los calificadores cv de nivel superior:
h<const int>(1, NULL); // el tipo de función es void(int, const int*) 
                       // z es const int, zp es const int*

Sobrecarga de plantillas de función

Las plantillas de función y las funciones no plantilla pueden sobrecargarse.

Una función no plantilla siempre es distinta de una especialización de plantilla con el mismo tipo. Las especializaciones de diferentes plantillas de función siempre son distintas entre sí incluso si tienen el mismo tipo. Dos plantillas de función con el mismo tipo de retorno y la misma lista de parámetros son distintas y pueden distinguirse por su lista de argumentos de plantilla explícita.

Cuando una expresión que utiliza parámetros de plantilla de tipo o constantes aparece en la lista de parámetros de la función o en el tipo de retorno, esa expresión permanece como parte de la firma de la plantilla de función para efectos de sobrecarga:

template<int I, int J>
A<I+J> f(A<I>, A<J>); // sobrecarga #1
template<int K, int L>
A<K+L> f(A<K>, A<L>); // igual que #1
template<int I, int J>
A<I-J> f(A<I>, A<J>); // sobrecarga #2

Dos expresiones que involucran parámetros de plantilla se denominan equivalentes si dos definiciones de función que contienen estas expresiones serían las mismas bajo la ODR , es decir, las dos expresiones contienen la misma secuencia de tokens cuyos nombres se resuelven a las mismas entidades mediante búsqueda de nombres, excepto que los parámetros de plantilla pueden tener nombres diferentes. Dos expresiones lambda nunca son equivalentes. (desde C++20)

template<int I, int J>
void f(A<I+J>); // sobrecarga de plantilla #1
template<int K, int L>
void f(A<K+L>); // equivalente a #1

Al determinar si dos expresiones dependientes son equivalentes, solo se consideran los nombres dependientes involucrados, no los resultados de la búsqueda de nombres. Si múltiples declaraciones de la misma plantilla difieren en el resultado de la búsqueda de nombres, se utiliza la primera de dichas declaraciones:

template<class T>
decltype(g(T())) h(); // decltype(g(T())) es un tipo dependiente
int g(int);
template<class T>
decltype(g(T())) h()
{                  // la redeclaración de h() utiliza la búsqueda anterior
    return g(T()); // aunque la búsqueda aquí sí encuentra g(int)
}
int i = h<int>(); // la sustitución del argumento de plantilla falla; g(int)
                  // no estaba en el ámbito en la primera declaración de h()

Dos plantillas de función se consideran equivalentes si

  • están declaradas en el mismo ámbito
  • tienen el mismo nombre
  • tienen equivalentes listas de parámetros de plantilla, lo que significa que las listas tienen la misma longitud, y para cada par de parámetros correspondiente, todo lo siguiente es cierto:
  • los dos parámetros son del mismo tipo (ambos tipos, ambas constantes, o ambas plantillas)
  • ambos son paquetes de parámetros o ninguno lo es
(desde C++11)
  • si son constantes, sus tipos son equivalentes,
  • si son plantillas, sus parámetros de plantilla son equivalentes,
  • si uno está declarado con nombre-de-concepto, ambos lo están, y los nombres-de-concepto son equivalentes.
(desde C++20)
  • las expresiones que involucran parámetros de plantilla en sus tipos de retorno y listas de parámetros son equivalentes
  • las expresiones en sus cláusulas requires que siguen a las listas de parámetros de plantilla, si están presentes, son equivalentes
  • las expresiones en sus cláusulas requires que siguen a los declaradores de función, si están presentes, son equivalentes
(desde C++20)

Dos potentially-evaluated (since C++20) expresiones que involucran parámetros de plantilla se denominan funcionalmente equivalentes si no son equivalentes , pero para cualquier conjunto dado de argumentos de plantilla, la evaluación de las dos expresiones resulta en el mismo valor.

Dos plantillas de función se consideran funcionalmente equivalentes si son equivalentes , excepto que una o más expresiones que involucran parámetros de plantilla en sus tipos de retorno y listas de parámetros son funcionalmente equivalentes .

Además, dos plantillas de función son funcionalmente equivalentes pero no equivalentes si sus restricciones se especifican de manera diferente, pero aceptan y son satisfechas por el mismo conjunto de listas de argumentos de plantilla.

(since C++20)

Si un programa contiene declaraciones de plantillas de función que son funcionalmente equivalentes pero no equivalentes , el programa está mal formado; no se requiere diagnóstico.

// equivalentes
template<int I>
void f(A<I>, A<I+10>); // sobrecarga #1
template<int I>
void f(A<I>, A<I+10>); // redeclaración de sobrecarga #1
// no equivalentes
template<int I>
void f(A<I>, A<I+10>); // sobrecarga #1
template<int I>
void f(A<I>, A<I+11>); // sobrecarga #2
// funcionalmente-equivalentes pero no equivalentes
// Este programa está mal formado, no se requiere diagnóstico
template<int I>
void f(A<I>, A<I+10>);      // sobrecarga #1
template<int I>
void f(A<I>, A<I+1+2+3+4>); // funcionalmente equivalente

Cuando la misma especialización de plantilla de función coincide con más de una plantilla de función sobrecargada (esto a menudo resulta de la deducción de argumentos de plantilla ), se realiza el ordenamiento parcial de plantillas de función sobrecargadas para seleccionar la coincidencia más adecuada.

Específicamente, el ordenamiento parcial ocurre en las siguientes situaciones:

1) resolución de sobrecarga para una llamada a una especialización de plantilla de función:
template<class X>
void f(X a);
template<class X>
void f(X* a);
int* p;
f(p);
2) cuando se toma la dirección de una especialización de plantilla de función :
template<class X>
void f(X a);
template<class X>
void f(X* a);
void (*p)(int*) = &f;
3) cuando un placement operator delete que es una especialización de plantilla de función es seleccionado para coincidir con un placement operator new:
4) cuando una declaración de función amiga , una instanciación explícita , o una especialización explícita hace referencia a una especialización de plantilla de función:
template<class X>
void f(X a);  // first template f
template<class X>
void f(X* a); // second template f
template<>
void f<>(int *a) {} // explicit specialization
// template argument deduction comes up with two candidates:
// f<int*>(int*) and f<int>(int*)
// partial ordering selects f<int>(int*) as more specialized

Informalmente, "A está más especializado que B" significa "A acepta menos tipos que B".

Formalmente, para determinar cuál de dos plantillas de función es más especializada, el proceso de ordenación parcial primero transforma una de las dos plantillas de la siguiente manera:

  • Para cada tipo, constante y parámetro de plantilla, incluyendo parameter packs, (since C++11) se genera un tipo, valor o plantilla ficticio único y se sustituye en el tipo de función de la plantilla
  • Si solo una de las dos plantillas de función que se comparan es una función miembro, y esa plantilla de función es un miembro no estático de alguna clase A , se inserta un nuevo primer parámetro en su lista de parámetros. Dado cv como los calificadores cv de la plantilla de función y ref como el calificador ref de la plantilla de función (since C++11) , el nuevo tipo de parámetro es cv A& a menos que ref sea && , o ref no esté presente y el primer parámetro de la otra plantilla tenga tipo de referencia a valor, en cuyo caso el tipo es cv A&& (since C++11) . Esto ayuda al ordenamiento de operadores, que se buscan tanto como funciones miembro como no miembro:
struct A {};
template<class T>
struct B
{
    template<class R>
    int operator*(R&); // #1
};
template<class T, class R>
int operator*(T&, R&); // #2
int main()
{
    A a;
    B<A> b;
    b * a; // la deducción de argumentos de plantilla para int B<A>::operator*(R&) da R=A
           //                             para int operator*(T&, R&), T=B<A>, R=A
    // Para el propósito de ordenamiento parcial, la plantilla miembro B<A>::operator*
    // se transforma en template<class R> int operator*(B<A>&, R&);
    // ordenamiento parcial entre
    //     int operator*(   T&, R&)  T=B<A>, R=A
    // e int operator*(B<A>&, R&)  R=A
    // selecciona int operator*(B<A>&, A&) como más especializada
}

Después de que una de las dos plantillas fue transformada como se describió anteriormente, template argument deduction se ejecuta utilizando la plantilla transformada como plantilla de argumento y el tipo de plantilla original de la otra plantilla como plantilla de parámetro. El proceso se repite luego utilizando la segunda plantilla (después de las transformaciones) como argumento y la primera plantilla en su forma original como parámetro.

Los tipos utilizados para determinar el orden dependen del contexto:

  • en el contexto de una llamada a función, los tipos son aquellos tipos de parámetros de función para los cuales la llamada tiene argumentos (los argumentos predeterminados de función, parameter packs, (since C++11) y los parámetros de elipsis no se consideran -- ver ejemplos abajo)
  • en el contexto de una llamada a una función de conversión definida por el usuario, se utilizan los tipos de retorno de las plantillas de función de conversión
  • en otros contextos, se utiliza el tipo de plantilla de función

Cada tipo de la lista anterior del parámetro de plantilla es deducido. Antes de que comience la deducción, cada parámetro P de la plantilla de parámetros y el argumento correspondiente A de la plantilla de argumentos se ajusta de la siguiente manera:

  • Si tanto P como A son tipos referencia antes, determinar cuál tiene más calificadores cv (en todos los demás casos, las calificaciones cv se ignoran para propósitos de ordenamiento parcial)
  • Si P es un tipo referencia, se reemplaza por el tipo referenciado
  • Si A es un tipo referencia, se reemplaza por el tipo referenciado
  • Si P está calificado cv, P se reemplaza por su versión sin calificadores cv
  • Si A está calificado cv, A se reemplaza por su versión sin calificadores cv

Después de estos ajustes, la deducción de P desde A se realiza siguiendo la deducción de argumentos de plantilla desde un tipo .

Si P es un paquete de parámetros de función, el tipo A de cada tipo de parámetro restante de la plantilla de argumento se compara con el tipo P del identificador-declarador del paquete de parámetros de función. Cada comparación deduce argumentos de plantilla para posiciones posteriores en los paquetes de parámetros de plantilla expandidos por el paquete de parámetros de función.

Si A fue transformado desde un paquete de parámetros de función, se compara con cada tipo de parámetro restante de la plantilla de parámetros.

(since C++11)

Si el argumento A de la plantilla transformada-1 puede utilizarse para deducir el parámetro correspondiente P de la plantilla-2, pero no viceversa, entonces este A está más especializado que P con respecto al(los) tipo(s) que son deducidos por este par P/A .

Si la deducción tiene éxito en ambas direcciones, y los tipos originales P y A eran tipos de referencia, entonces se realizan pruebas adicionales:

  • Si A era una referencia lvalue y P era una referencia rvalue, A se considera más especializada que P
  • Si A estaba más calificada cv que P , A se considera más especializada que P

En todos los demás casos, ninguna plantilla está más especializada que la otra con respecto al(los) tipo(s) deducido(s) por este P/A par.

Después de considerar cada P y A en ambas direcciones, si, para cada tipo que fue considerado,

  • template-1 es al menos tan especializado como template-2 para todos los tipos
  • template-1 es más especializado que template-2 para algunos tipos
  • template-2 no es más especializado que template-1 para ningún tipo O no es al menos tan especializado para ningún tipo

Entonces template-1 está más especializado que template-2. Si las condiciones anteriores son verdaderas después de cambiar el orden de las plantillas, entonces template-2 está más especializado que template-1. De lo contrario, ninguna plantilla está más especializada que la otra.

En caso de empate, si una plantilla de función tiene un paquete de parámetros final y la otra no, la que tiene el parámetro omitido se considera más especializada que la que tiene el paquete de parámetros vacío.

(since C++11)

Si, después de considerar todos los pares de plantillas sobrecargadas, hay una que es inequívocamente más especializada que todas las demás, se selecciona la especialización de esa plantilla; de lo contrario, la compilación falla.

En los siguientes ejemplos, los argumentos ficticios se llamarán U1, U2:

template<class T>
void f(T);        // plantilla #1
template<class T>
void f(T*);       // plantilla #2
template<class T>
void f(const T*); // plantilla #3
void m()
{
    const int* p;
    f(p); // la resolución de sobrecarga selecciona: #1: void f(T ) [T = const int *]
          //                            #2: void f(T*) [T = const int]
          //                            #3: void f(const T *) [T = int]
    // ordenamiento parcial:
    // #1 desde #2 transformada: void(T) desde void(U1*): P=T A=U1*: deducción ok: T=U1*
    // #2 desde #1 transformada: void(T*) desde void(U1): P=T* A=U1: deducción falla
    // #2 es más especializada que #1 con respecto a T
    // #1 desde #3 transformada: void(T) desde void(const U1*): P=T, A=const U1*: ok
    // #3 desde #1 transformada: void(const T*) desde void(U1): P=const T*, A=U1: falla
    // #3 es más especializada que #1 con respecto a T
    // #2 desde #3 transformada: void(T*) desde void(const U1*): P=T* A=const U1*: ok
    // #3 desde #2 transformada: void(const T*) desde void(U1*): P=const T* A=U1*: falla
    // #3 es más especializada que #2 con respecto a T
    // resultado: #3 es seleccionada
    // en otras palabras, f(const T*) es más especializada que f(T) o f(T*)
}
template<class T>
void f(T, T*);   // #1
template<class T>
void f(T, int*); // #2
void m(int* p)
{
    f(0, p); // deducción para #1: void f(T, T*) [T = int]
             // deducción para #2: void f(T, int*) [T = int]
    // ordenamiento parcial:
    // #1 desde #2: void(T,T*) desde void(U1,int*): P1=T, A1=U1: T=U1
    //                                            P2=T*, A2=int*: T=int: falla
    // #2 desde #1: void(T,int*) desde void(U1,U2*): P1=T A1=U1: T=U1
    //                                             P2=int* A2=U2*: falla
    // ninguna es más especializada con respecto a T, la llamada es ambigua
}
template<class T>
void g(T);  // plantilla #1
template<class T>
void g(T&); // plantilla #2
void m()
{
    float x;
    g(x); // deducción desde #1: void g(T ) [T = float]
          // deducción desde #2: void g(T&) [T = float]
    // ordenamiento parcial:
    // #1 desde #2: void(T) desde void(U1&): P=T, A=U1 (tras ajuste), ok
    // #2 desde #1: void(T&) desde void(U1): P=T (tras ajuste), A=U1: ok
    // ninguna es más especializada respecto a T, la llamada es ambigua
}
template<class T>
struct A { A(); };
template<class T>
void h(const T&); // #1
template<class T>
void h(A<T>&);    // #2
void m()
{
    A<int> z;
    h(z); // deducción desde #1: void h(const T &) [T = A<int>]
          // deducción desde #2: void h(A<T> &) [T = int]
    // ordenamiento parcial:
    // #1 desde #2: void(const T&) desde void(A<U1>&): P=T A=A<U1>: ok T=A<U1>
    // #2 desde #1: void(A<T>&) desde void(const U1&): P=A<T> A=const U1: falla
    // #2 está más especializada que #1 con respecto a T
    const A<int> z2;
    h(z2); // deducción desde #1: void h(const T&) [T = A<int>]
           // deducción desde #2: void h(A<T>&) [T = int], pero la sustitución falla
    // solo una sobrecarga para elegir, ordenamiento parcial no intentado, #1 es llamada
}

Dado que un contexto de llamada considera solo los parámetros para los cuales hay argumentos de llamada explícitos, aquellos paquetes de parámetros de función, (since C++11) parámetros de elipsis, y parámetros con argumentos predeterminados, para los cuales no hay argumento de llamada explícito, son ignorados:

template<class T>
void f(T);         // #1
template<class T>
void f(T*, int = 1); // #2
void m(int* ip)
{
    int* ip;
    f(ip); // llama a #2 (T* está más especializado que T)
}
template<class T>
void g(T);       // #1
template<class T>
void g(T*, ...); // #2
void m(int* ip)
{
    g(ip); // llama a #2 (T* está más especializado que T)
}
template<class T, class U>
struct A {};
template<class T, class U>
void f(U, A<U, T>* p = 0); // #1
template<class U>
void f(U, A<U, U>* p = 0); // #2
void h()
{
    f<int>(42, (A<int, int>*)0); // llama a #2
    f<int>(42);                  // error: ambiguo
}
template<class T>
void g(T, T = T()); // #1
template<class T, class... U>
void g(T, U...);    // #2
void h()
{
    g(42); // error: ambiguo
}
template<class T, class... U>
void f(T, U...); // #1
template<class T>
void f(T);       // #2
void h(int i)
{
    f(&i); // llama a #2 debido al desempate entre el paquete de parámetros y ningún parámetro
           // (nota: era ambiguo entre DR692 y DR1395)
}
template<class T, class... U>
void g(T*, U...); // #1
template<class T>
void g(T);        // #2
void h(int i)
{
    g(&i); // OK: llama a #1 (T* está más especializado que T)
}
template<class... T>
int f(T*...);    // #1
template<class T>
int f(const T&); // #2
f((int*)0); // CORRECTO: selecciona #2; la plantilla no variádica es más especializada que
            // la plantilla variádica (era ambigua antes de DR1395 porque la deducción
            // fallaba en ambas direcciones)
template<class... Args>
void f(Args... args);        // #1
template<class T1, class... Args>
void f(T1 a1, Args... args); // #2
template<class T1, class T2>
void f(T1 a1, T2 a2);        // #3
f();        // llama a #1
f(1, 2, 3); // llama a #2
f(1, 2);    // llama a #3; la plantilla no variádica #3 es más
            // especializada que las plantillas variádicas #1 y #2

Durante la deducción de argumentos de plantilla en el proceso de ordenación parcial, los parámetros de plantilla no requieren coincidir con los argumentos, si el argumento no se utiliza en ninguno de los tipos considerados para la ordenación parcial

template<class T>
T f(int); // #1
template<class T, class U>
T f(U);   // #2
void g()
{
    f<int>(1); // especialización de #1 es explícita: T f(int) [T = int]
               // especialización de #2 es deducida:  T f(U) [T = int, U = int]
    // ordenamiento parcial (solo considerando el tipo de argumento):
    // #1 desde #2: T(int) desde U1(U2): falla
    // #2 desde #1: T(U) desde U1(int): ok: U=int, T sin usar
    // llama a #1
}

El ordenamiento parcial de plantillas de función que contienen paquetes de parámetros de plantilla es independiente del número de argumentos deducidos para esos paquetes de parámetros de plantilla.

template<class...>
struct Tuple {};
template<class... Types>
void g(Tuple<Types...>);      // #1
template<class T1, class... Types>
void g(Tuple<T1, Types...>);  // #2
template<class T1, class... Types>
void g(Tuple<T1, Types&...>); // #3
g(Tuple<>());            // llama a #1
g(Tuple<int, float>());  // llama a #2
g(Tuple<int, float&>()); // llama a #3
g(Tuple<int>());         // llama a #3
(desde C++11)

Para compilar una llamada a una función template, el compilador debe decidir entre las sobrecargas no template, las sobrecargas template y las especializaciones de las sobrecargas template.

template<class T>
void f(T);      // #1: sobrecarga de plantilla
template<class T>
void f(T*);     // #2: sobrecarga de plantilla
void f(double); // #3: sobrecarga no plantilla
template<>
void f(int);    // #4: especialización de #1
f('a');        // llama a #1
f(new int(1)); // llama a #2
f(1.0);        // llama a #3
f(1);          // llama a #4

Sobrecargas de función vs especializaciones de función

Tenga en cuenta que solo las sobrecargas no plantilla y las plantillas principales participan en la resolución de sobrecargas. Las especializaciones no son sobrecargas y no se consideran. Solo después de que la resolución de sobrecargas selecciona la plantilla de función principal que mejor coincide, se examinan sus especializaciones para ver si alguna coincide mejor.

template<class T>
void f(T);    // #1: sobrecarga para todos los tipos
template<>
void f(int*); // #2: especialización de #1 para punteros a int
template<class T>
void f(*T);   // #3: sobrecarga para todos los tipos de puntero
f(new int(1)); // llama a #3, aunque la especialización de #1 sería una coincidencia perfecta

Es importante recordar esta regla al ordenar los archivos de cabecera de una unidad de traducción. Para más ejemplos de la interacción entre sobrecargas de funciones y especializaciones de funciones, expanda a continuación:

Ejemplos

Consideremos primero algunos escenarios donde la búsqueda dependiente de argumento no se emplea. Para ello, utilizamos la llamada ( f ) ( t ) . Como se describe en ADL , envolver el nombre de la función entre paréntesis suprime la búsqueda dependiente de argumento.

  • Múltiples sobrecargas de f ( ) declaradas antes del punto-de-referencia (POR) en g ( ) .
#include <iostream>
struct A {};
template<class T>
void f(T)  { std::cout << "#1\n"; } // sobrecarga #1 antes de f() POR
template<class T>
void f(T*) { std::cout << "#2\n"; } // sobrecarga #2 antes de f() POR
template<class T>
void g(T* t) 
{
    (f)(t); // f() POR
}
int main()
{
    A* p = nullptr;
    g(p); // POR de g() y f()
}
// Tanto #1 como #2 se añaden a la lista de candidatos;
// #2 es seleccionada porque es una coincidencia mejor.

Salida:

#2


  • Una mejor coincidencia de plantilla sobrecargada se declara después de POR.
#include <iostream>
struct A {};
template<class T>
void f(T)  { std::cout << "#1\n"; } // #1
template<class T>
void g(T* t) 
{
    (f)(t); // f() POR
}
template<class T>
void f(T*) { std::cout << "#2\n"; } // #2
int main()
{
    A* p = nullptr;
    g(p); // POR de g() y f()
}
// Solo #1 se añade a la lista de candidatos; #2 se define después del POR;
// por lo tanto, no se considera para la sobrecarga aunque sea una coincidencia mejor.

Salida:

#1


  • Una especialización de plantilla explícita con mejor coincidencia se declara después de POR.
#include <iostream>
struct A {};
template<class T>
void f(T)    { std::cout << "#1\n"; } // #1
template<class T>
void g(T* t) 
{
    (f)(t); // f() POR
}
template<>
void f<>(A*) { std::cout << "#3\n"; } // #3
int main()
{
    A* p = nullptr;
    g(p); // POR of g() and f()
}
// #1 is added to the candidate list; #3 is a better match defined after POR. The
// candidate list consists of #1 which is eventually selected. After that, the explicit 
// specialization #3 of #1 declared after POI is selected because it is a better match. 
// This behavior is governed by 14.7.3/6 [temp.expl.spec] and has nothing to do with ADL.

Salida:

#3


  • Una mejor coincidencia de sobrecarga de plantilla se declara después de POR. La mejor coincidencia de especialización explícita de plantilla se declara después de la mejor sobrecarga coincidente.
#include <iostream>
struct A {};
template<class T>
void f(T)    { std::cout << "#1\n"; } // #1
template<class T>
void g(T* t) 
{
    (f)(t); // f() POR
}
template<class T>
void f(T*)   { std::cout << "#2\n"; } // #2
template<>
void f<>(A*) { std::cout << "#3\n"; } // #3
int main()
{
    A* p = nullptr;
    g(p); // POR de g() y f()
}
// #1 es el único miembro de la lista de candidatos y finalmente es seleccionado.
// Después de esto, la especialización explícita #3 se omite porque realmente
// especializa #2 declarado después del POR.

Salida:

#1


Consideremos ahora aquellos casos que emplean la búsqueda dependiente de argumento (es decir, utilizamos el formato de llamada más común f ( t ) ).

  • Una sobrecarga de plantilla con mejor coincidencia se declara después de POR.
#include <iostream>
struct A {};
template<class T>
void f(T)  { std::cout << "#1\n"; } // #1
template<class T>
void g(T* t) 
{
    f(t); // f() POR
}
template<class T>
void f(T*) { std::cout << "#2\n"; } // #2
int main()
{
    A* p = nullptr;
    g(p); // POR de g() y f()
}
// #1 se añade a la lista de candidatos como resultado de la búsqueda ordinaria;
// #2 se define después de POR pero se añade a la lista de candidatos mediante búsqueda ADL.
// #2 se selecciona por ser la mejor coincidencia.

Salida:

#2


  • Una mejor coincidencia de sobrecarga de plantilla se declara después de POR. La mejor coincidencia de especialización explícita de plantilla se declara antes de la mejor coincidencia de sobrecarga.
#include <iostream>
struct A {};
template<class T>
void f(T)    { std::cout << "#1\n"; } // #1
template<class T>
void g(T* t) 
{
    f(t); // f() POR
}
template<>
void f<>(A*) { std::cout << "#3\n"; } // #3
template<class T>
void f(T*)   { std::cout << "#2\n"; } // #2
int main()
{
    A* p = nullptr;
    g(p); // POR of g() and f()
}
// #1 is added to the candidate list as a result of the ordinary lookup;
// #2 is defined after POR but it is added to the candidate list via ADL lookup.
// #2 is selected among the primary templates, being the better match.
// Since #3 is declared before #2, it is an explicit specialization of #1.
// Hence the final selection is #2.

Salida:

#2


  • Una mejor coincidencia de sobrecarga de plantilla se declara después de POR. La mejor coincidencia de especialización explícita de plantilla se declara al final.
#include <iostream>
struct A {};
template<class T>
void f(T)    { std::cout << "#1\n"; } // #1
template<class T>
void g(T* t) 
{
    f(t); // f() POR
}
template<class T>
void f(T*)   { std::cout << "#2\n"; } // #2
template<>
void f<>(A*) { std::cout << "#3\n"; } // #3
int main()
{
    A* p = nullptr;
    g(p); // POR de g() y f()
}
// #1 se añade a la lista de candidatos como resultado de la búsqueda ordinaria;
// #2 se define después de POR pero se añade a la lista de candidatos mediante búsqueda ADL.
// #2 se selecciona entre las plantillas principales, siendo la mejor coincidencia.
// Dado que #3 se declara después de #2, es una especialización explícita de #2;
// por lo tanto, se selecciona como la función a llamar.

Salida:

#3


Siempre que los argumentos sean algunos tipos básicos de C++, no hay espacios de nombres asociados a ADL. Por lo tanto, esos escenarios son idénticos a los ejemplos sin ADL anteriores.

Para reglas detalladas sobre la resolución de sobrecarga, consulte overload resolution .

Especialización de plantilla de función

Palabras clave

template , extern (desde C++11)

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 214 C++98 no se especificaba el procedimiento exacto del ordenamiento parcial especificación añadida
CWG 532 C++98 no se especificaba el orden entre una plantilla de función miembro no estática
y una plantilla de función no miembro
especificación añadida
CWG 581 C++98 se permitía la lista de argumentos de plantilla en una especialización explícita o
instanciación de una plantilla de constructor
prohibido
CWG 1321 C++98 no estaba claro si los mismos nombres dependientes en la
primera declaración y una redeclaración son equivalentes
son equivalentes y
el significado es el mismo que
en la primera declaración
CWG 1395 C++11 la deducción fallaba cuando A provenía de un pack,
y no existía un desempate para pack vacío
deducción permitida,
desempate añadido
CWG 1406 C++11 el tipo del nuevo primer parámetro añadido para
una plantilla de función miembro no estática no
estaba relacionado con el ref-qualifier de esa plantilla
el tipo es una referencia a valor
derecho (rvalue reference) si el
ref-qualifier es &&
CWG 1446 C++11 el tipo del nuevo primer parámetro añadido para una plantilla de función
miembro no estática sin ref-qualifier era una referencia a valor izquierdo,
incluso si esa plantilla de función miembro se comparaba con una
plantilla de función cuyo primer parámetro tenía tipo referencia a valor derecho
el tipo es una
referencia a valor derecho
(rvalue reference) en este caso
CWG 2373 C++98 se añadían nuevos primeros parámetros a las listas de parámetros
de plantillas de funciones miembro estáticas en el ordenamiento parcial
no se añaden

Véase también