Template arguments
Para que una plantilla sea instanciada, cada parámetro de plantilla debe ser reemplazado por un argumento de plantilla correspondiente. Los argumentos son proporcionados explícitamente, deducidos o establecidos por defecto.
Cada parámetro en template-parameter-list (ver sintaxis de identificador de plantilla ) pertenece a una de las siguientes categorías:
- argumento de plantilla constante
- argumento de plantilla de tipo
- argumento de plantilla de plantilla
Contenidos |
Argumentos de plantilla constantes
También conocido como non-type template arguments (ver abajo ).
|
El argumento de plantilla que puede utilizarse con un parámetro de plantilla constante puede ser cualquier expresión manifiestamente constante-evaluada . |
(until C++11) |
|
El argumento de plantilla que puede utilizarse con un parámetro de plantilla constante puede ser cualquier cláusula de inicialización . Si la cláusula de inicialización es una expresión, debe ser manifiestamente constante-evaluada . |
(since C++11) |
Dado el
tipo
de la
declaración del parámetro de plantilla constante
como
T
y el argumento de plantilla proporcionado para el parámetro como
E
.
|
La declaración inventada T x = E ; debe satisfacer las restricciones semánticas para la definición de una constexpr variable con duración de almacenamiento estático . |
(desde C++20) |
|
Si
Si un tipo de parámetro deducido no es un tipo estructural , el programa está mal formado. Para paquetes de parámetros de plantilla constantes cuyo tipo utiliza un tipo de marcador de posición, el tipo se deduce independientemente para cada argumento de plantilla y no necesita coincidir. |
(desde C++17) |
template<auto n> struct B { /* ... */ }; B<5> b1; // OK: el tipo del parámetro de plantilla constante es int B<'a'> b2; // OK: el tipo del parámetro de plantilla constante es char B<2.5> b3; // error (hasta C++20): el tipo del parámetro de plantilla constante no puede ser double // Marcador de posición de tipo de clase deducido en C++20, los argumentos de plantilla de clase se deducen en el // sitio de llamada template<std::array arr> void f(); f<std::array<double, 8>{}>(); template<auto...> struct C {}; C<'C', 0, 2L, nullptr> x; // OK
El valor de un parámetro de plantilla constante
P
de tipo
(posiblemente deducido)
(since C++17)
T
se determina a partir de su argumento de plantilla
A
de la siguiente manera:
|
(hasta C++11) |
|
(desde C++11)
(hasta C++20) |
|
(desde C++20) |
template<int i> struct C { /* ... */ }; C<{42}> c1; // CORRECTO template<auto n> struct B { /* ... */ }; struct J1 { J1* self = this; }; B<J1{}> j1; // error: la inicialización del objeto parámetro de plantilla // no es una expresión constante struct J2 { J2 *self = this; constexpr J2() {} constexpr J2(const J2&) {} }; B<J2{}> j2; // error: el objeto parámetro de plantilla no es // equivalente-por-argumento-de-plantilla al temporal introducido
|
Las siguientes limitaciones aplican al instanciar plantillas que tienen parámetros de plantilla constantes:
En particular, esto implica que los literales de cadena, las direcciones de elementos de array y las direcciones de miembros no estáticos no pueden usarse como argumentos de plantilla para instanciar plantillas cuyos correspondientes parámetros de plantilla constantes son punteros a objetos. |
(hasta C++17) |
|
los parámetros de plantilla constantes de tipo referencia o puntero y los miembros de datos no estáticos de tipo referencia o puntero en un parámetro de plantilla constante de tipo clase y sus subobjetos (desde C++20) no pueden referir a/ser la dirección de
|
(desde C++17) |
template<const int* pci> struct X {}; int ai[10]; X<ai> xi; // CORRECTO: conversión de array a puntero y conversión de calificación cv struct Y {}; template<const Y& b> struct Z {}; Y y; Z<y> z; // CORRECTO: sin conversión template<int (&pa)[5]> struct W {}; int b[5]; W<b> w; // CORRECTO: sin conversión void f(char); void f(int); template<void (*pf)(int)> struct A {}; A<&f> a; // CORRECTO: la resolución de sobrecarga selecciona f(int)
template<class T, const char* p> class X {}; X<int, "Studebaker"> x1; // error: literal de cadena como argumento de plantilla template<int* p> class X {}; int a[10]; struct S { int m; static int s; } s; X<&a[2]> x3; // error (hasta C++20): dirección de elemento de array X<&s.m> x4; // error (hasta C++20): dirección de miembro no estático X<&s.s> x5; // OK: dirección de miembro estático X<&S::s> x6; // OK: dirección de miembro estático template<const int& CRI> struct B {}; B<1> b2; // error: se requeriría un temporal para el argumento de plantilla int c = 1; B<c> b1; // OK
Argumentos de plantilla de tipo
Un argumento de plantilla para un parámetro de plantilla de tipo debe ser un type-id , que puede nombrar un tipo incompleto:
template<typename T> class X {}; // plantilla de clase struct A; // tipo incompleto typedef struct {} B; // alias de tipo para un tipo sin nombre int main() { X<A> x1; // OK: 'A' nombra un tipo X<A*> x2; // OK: 'A*' nombra un tipo X<B> x3; // OK: 'B' nombra un tipo }
Argumentos de plantilla de plantilla
Un argumento de plantilla para un parámetro de plantilla de plantilla debe ser una expresión de identificador que nombre una plantilla de clase o un alias de plantilla.
Cuando el argumento es una plantilla de clase, solo se considera la plantilla principal al hacer coincidir el parámetro. Las especializaciones parciales, si las hay, solo se consideran cuando se instancia una especialización basada en este parámetro de plantilla de plantilla.
template<typename T> // plantilla principal class A { int x; }; template<typename T> // especialización parcial class A<T*> { long x; }; // plantilla de clase con un parámetro de plantilla V template<template<typename> class V> class C { V<int> y; // utiliza la plantilla principal V<int*> z; // utiliza la especialización parcial }; C<A> c; // c.y.x tiene tipo int, c.z.x tiene tipo long
Para que un argumento de plantilla de plantilla
A
coincida con un parámetro de plantilla de plantilla
P
,
P
debe ser
al menos tan especializado
como
A
(ver más abajo).
Si la lista de parámetros de
P
incluye un
paquete de parámetros
, cero o más parámetros de plantilla (o paquetes de parámetros) de la lista de parámetros de plantilla de
A
son emparejados por él.
(desde C++11)
Formalmente, un parámetro de plantilla de plantilla
P
es
al menos tan especializado
como un argumento de plantilla de plantilla
A
si, dada la siguiente reescritura a dos plantillas de función, la plantilla de función correspondiente a
P
es al menos tan especializada como la plantilla de función correspondiente a
A
de acuerdo con las reglas de ordenamiento parcial para
plantillas de función
. Dada una plantilla de clase inventada
X
con la lista de parámetros de plantilla de
A
(incluyendo argumentos por defecto):
-
Cada una de las dos plantillas de función tiene los mismos parámetros de plantilla, respectivamente, que
PoA. -
Cada plantilla de función tiene un único parámetro de función cuyo tipo es una especialización de
Xcon argumentos de plantilla correspondientes a los parámetros de plantilla de la respectiva plantilla de función donde, para cada parámetro de plantillaPPen la lista de parámetros de plantilla de la plantilla de función, se forma un argumento de plantilla correspondienteAA. SiPPdeclara un paquete de parámetros, entoncesAAes la expansión de paquetePP...; de lo contrario, (since C++11)AAes la expresión de identificaciónPP.
Si la reescritura produce un tipo inválido, entonces
P
no es al menos tan especializado como
A
.
template<typename T> struct eval; // plantilla principal template<template<typename, typename...> class TT, typename T1, typename... Rest> struct eval<TT<T1, Rest...>> {}; // especialización parcial de eval template<typename T1> struct A; template<typename T1, typename T2> struct B; template<int N> struct C; template<typename T1, int N> struct D; template<typename T1, typename T2, int N = 17> struct E; eval<A<int>> eA; // OK: coincide con la especialización parcial de eval eval<B<int, float>> eB; // OK: coincide con la especialización parcial de eval eval<C<17>> eC; // error: C no coincide con TT en la especialización parcial // porque el primer parámetro de TT es un // parámetro de plantilla de tipo, mientras que 17 no nombra un tipo eval<D<int, 17>> eD; // error: D no coincide con TT en la especialización parcial // porque el segundo parámetro de TT es un // paquete de parámetros de tipo, mientras que 17 no nombra un tipo eval<E<int, float>> eE; // error: E no coincide con TT en la especialización parcial // porque el tercer parámetro (por defecto) de E es una constante
Antes de la adopción de
P0522R0
, cada uno de los parámetros de plantilla de
A
debía coincidir exactamente con los parámetros de plantilla correspondientes de
P
. Esto impedía que muchos argumentos de plantilla razonables fueran aceptados.
Aunque fue señalado muy temprano ( CWG#150 ), para cuando se resolvió, los cambios se aplicaron al documento de trabajo de C++17 y la resolución se convirtió en una característica de facto de C++17. Muchos compiladores lo deshabilitan por defecto:
- GCC lo desactiva en todos los modos de lenguaje anteriores a C++17 por defecto, solo puede habilitarse configurando un flag del compilador en estos modos.
- Clang lo desactiva en todos los modos de lenguaje por defecto, solo puede habilitarse configurando un flag del compilador.
- Microsoft Visual Studio lo trata como una característica normal de C++17 y solo lo habilita en modos de lenguaje C++17 y posteriores (es decir, no hay soporte en el modo de lenguaje C++14, que es el modo predeterminado).
template<class T> class A { /* ... */ }; template<class T, class U = T> class B { /* ... */ }; template<class... Types> class C { /* ... */ }; template<template<class> class P> class X { /* ... */ }; X<A> xa; // CORRECTO X<B> xb; // CORRECTO después de P0522R0 // Error anteriormente: no coincide exactamente X<C> xc; // CORRECTO después de P0522R0 // Error anteriormente: no coincide exactamente template<template<class...> class Q> class Y { /* ... */ }; Y<A> ya; // CORRECTO Y<B> yb; // CORRECTO Y<C> yc; // CORRECTO template<auto n> class D { /* ... */ }; // nota: C++17 template<template<int> class R> class Z { /* ... */ }; Z<D> zd; // CORRECTO después de P0522R0: el parámetro de plantilla // está más especializado que el argumento de plantilla template<int> struct SI { /* ... */ }; template<template<auto> class> void FA(); // nota: C++17 FA<SI>(); // Error
Equivalencia de argumentos de plantilla
La equivalencia de argumentos de plantilla se utiliza para determinar si dos identificadores de plantilla son iguales.
Dos valores son template-argument-equivalent si son del mismo tipo y se cumple alguna de las siguientes condiciones:
- Son de tipo entero o enumeración y sus valores son los mismos.
- Son de tipo puntero y tienen el mismo valor de puntero.
- Son de tipo puntero-a-miembro y se refieren al mismo miembro de clase o ambos son el valor de puntero a miembro nulo.
- Son de tipo referencia a lvalue y se refieren al mismo objeto o función.
|
(desde C++11) |
|
(desde C++20) |
Resolución de ambigüedad
Si un argumento de plantilla puede ser interpretado tanto como un type-id como una expresión, siempre se interpreta como un type-id, incluso si el parámetro de plantilla correspondiente es constante:
template<class T> void f(); // #1 template<int I> void f(); // #2 void g() { f<int()>(); // "int()" es tanto un tipo como una expresión, // llama a #1 porque se interpreta como un tipo }
Notas
Antes de C++26, los argumentos de plantilla constantes se denominaban argumentos de plantilla no tipo en la terminología estándar. La terminología fue cambiada por P2841R6 / PR #7587 .
| Macro de prueba de características | Valor | Std | Característica |
|---|---|---|---|
__cpp_template_template_args
|
201611L
|
(C++17)
(DR) |
Coincidencia de argumentos de plantilla de plantilla |
__cpp_nontype_template_args
|
201411L
|
(C++17) | Permitir evaluación constante para todos argumentos de plantilla constantes |
201911L
|
(C++20) | Tipos de clase y tipos de punto flotante en parámetros de plantilla constantes |
Ejemplo
|
Esta sección está incompleta
Razón: sin ejemplo |
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 150
( P0522R0 ) |
C++98 |
los argumentos template-template debían coincidir exactamente con las listas
de parámetros de los parámetros template-template |
también se permite
el más especializado |
| CWG 354 | C++98 | los valores de puntero nulo no podían ser argumentos de plantilla constantes | permitido |
| CWG 1398 | C++11 |
los argumentos de plantilla constantes no podían tener tipo
std::nullptr_t
|
permitido |
| CWG 1570 | C++98 | los argumentos de plantilla constantes podían designar direcciones de subobjetos | no permitido |
| P2308R1 |
C++11
C++20 |
1. la inicialización de lista no estaba permitida para
argumentos de plantilla constantes (C++11) 2. no estaba claro cómo se inicializan los parámetros de plantilla constantes de tipos de clase (C++20) |
1. permitido
2. aclarado |