Function template
Una plantilla de función define una familia de funciones.
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) |
|
|
(hasta C++11) |
Plantilla de función abreviadaCuando 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) | |||||||
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.
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)
|
(desde C++11) |
-
- si son constantes, sus tipos son equivalentes,
- si son plantillas, sus parámetros de plantilla 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
|
(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:
template<class X> void f(X a); template<class X> void f(X* a); int* p; f(p);
template<class X> void f(X a); template<class X> void f(X* a); void (*p)(int*) = &f;
|
Esta sección está incompleta
Razón: mini-ejemplo |
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 cvA&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 cvA&&(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
PcomoAson 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
Pes un tipo referencia, se reemplaza por el tipo referenciado -
Si
Aes un tipo referencia, se reemplaza por el tipo referenciado -
Si
Pestá calificado cv,Pse reemplaza por su versión sin calificadores cv -
Si
Aestá calificado cv,Ase 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
Si
|
(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
Aera una referencia lvalue yPera una referencia rvalue,Ase considera más especializada queP -
Si
Aestaba más calificada cv queP,Ase considera más especializada queP
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) |
|
Esta sección está incompleta
Razón: 14.8.3[temp.over] |
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.
Ejecutar este código
#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
Ejecutar este código
#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
Ejecutar este código
#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
Ejecutar este código
#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
Ejecutar este código
#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
Ejecutar este código
#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
Ejecutar este código
#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
|
Para reglas detalladas sobre la resolución de sobrecarga, consulte overload resolution .
Especialización de plantilla de función
|
Esta sección está incompleta
Razón: 14.8[temp.fct.spec] (nótese que 14.8.1[temp.arg.explicit] ya está en el artículo de especialización completa: o los detalles de función van aquí: falta de parciales, interacción con sobrecargas de función, o simplemente referirse a eso |
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 |