Template parameters
Cada template está parametrizado por uno o más parámetros de template.
Cada parámetro en template-parameter-list (ver sintaxis de declaración de plantilla ) pertenece a una de las siguientes categorías:
- parámetro de plantilla constante
- parámetro de plantilla de tipo
- parámetro de plantilla de plantilla
Contenidos |
Parámetro de plantilla constante
También conocido como non-type template parameter (ver abajo ).
| type name (opcional) | (1) | ||||||||
type
name
(opcional)
=
default
|
(2) | ||||||||
type
...
name
(opcional)
|
(3) | (since C++11) | |||||||
| type | - |
uno de los siguientes tipos:
|
||||
| name | - | el nombre del parámetro de plantilla constante | ||||
| default | - | el default template argument |
Un
tipo estructural
es uno de los siguientes tipos (opcionalmente calificado con cv, las calificaciones se ignoran):
- tipo de referencia lvalue (a objeto o a función);
- un tipo integral ;
- un tipo puntero (a objeto o a función);
- un tipo puntero a miembro (a miembro objeto o a miembro función);
- un tipo enumeración ;
| (desde C++11) |
|
(desde C++20) |
Los tipos de array y función pueden escribirse en una declaración de plantilla, pero son automáticamente reemplazados por puntero a objeto y puntero a función según corresponda.
Cuando el nombre de un parámetro de plantilla constante se utiliza en una expresión dentro del cuerpo de la plantilla de clase, es un prvalue inmodificable a menos que su tipo fuera un tipo de referencia a lvalue , o a menos que su tipo sea un tipo de clase (desde C++20) .
Un parámetro de plantilla de la forma
class
Foo
no es un parámetro de plantilla constante sin nombre de tipo
Foo
, incluso si en otros contextos
class
Foo
es un
especificador de tipo elaborado
y
class
Foo x
;
declara que
x
es de tipo
Foo
.
|
Un
identificador
que nombra un parámetro de plantilla constante de tipo clase
struct A { friend bool operator==(const A&, const A&) = default; }; template<A a> void f() { &a; // OK const A& ra = a, &rb = a; // Ambos vinculados al mismo objeto de parámetro de plantilla assert(&ra == &rb); // pasa } |
(desde C++20) |
Parámetro de plantilla de tipo
| type-parameter-key name (opcional) | (1) | ||||||||
type-parameter-key
name
(opcional)
=
default
|
(2) | ||||||||
type-parameter-key
...
name
(opcional)
|
(3) | (desde C++11) | |||||||
| type-constraint name (opcional) | (4) | (desde C++20) | |||||||
type-constraint
name
(opcional)
=
default
|
(5) | (desde C++20) | |||||||
type-constraint
...
name
(opcional)
|
(6) | (desde C++20) | |||||||
| type-parameter-key | - |
ya sea
typename
o
class
. No hay diferencia entre estas palabras clave en una declaración de parámetro de plantilla de tipo
|
| type-constraint | - | ya sea el nombre de un concepto o el nombre de un concepto seguido de una lista de argumentos de plantilla (entre corchetes angulares). En cualquier caso, el nombre del concepto puede estar opcionalmente calificado |
| name | - | el nombre del parámetro de plantilla de tipo |
| default | - | el argumento de plantilla predeterminado |
template<class T> class My_vector { /* ... */ };
template<class T = void> struct My_op_functor { /* ... */ };
template<typename... Ts> class My_tuple { /* ... */ };
template<My_concept T> class My_constrained_vector { /* ... */ };
template<My_concept T = void> class My_constrained_op_functor { /* ... */ };
template<My_concept... Ts> class My_constrained_tuple { /* ... */ };
El nombre del parámetro es opcional:
// Declaraciones de las plantillas mostradas anteriormente: template<class> class My_vector; template<class = void> struct My_op_functor; template<typename...> class My_tuple;
En el cuerpo de la declaración de plantilla, el nombre de un parámetro de tipo es un typedef-name que alias el tipo suministrado cuando la plantilla es instanciada.
|
Cada parámetro restringido
template<typename T> concept C1 = true; template<typename... Ts> // variadic concept concept C2 = true; template<typename T, typename U> concept C3 = true; template<C1 T> struct s1; // constraint-expression is C1<T> template<C1... T> struct s2; // constraint-expression is (C1<T> && ...) template<C2... T> struct s3; // constraint-expression is (C2<T> && ...) template<C3<int> T> struct s4; // constraint-expression is C3<T, int> template<C3<int>... T> struct s5; // constraint-expression is (C3<T, int> && ...) |
(desde C++20) |
Parámetro de plantilla de plantilla
template
<
lista-de-parámetros
>
clave-tipo-parámetro
nombre
(opcional)
|
(1) | ||||||||
template
<
lista-de-parámetros
>
clave-tipo-parámetro
nombre
(opcional)
=
predeterminado
|
(2) | ||||||||
template
<
lista-de-parámetros
>
clave-tipo-parámetro
...
nombre
(opcional)
|
(3) | (desde C++11) | |||||||
| type-parameter-key | - |
class
o
typename
(desde C++17)
|
En el cuerpo de la declaración de plantilla, el nombre de este parámetro es un nombre-de-plantilla (y necesita argumentos para ser instanciado).
template<typename T> class my_array {}; // dos parámetros de plantilla de tipo y un parámetro de plantilla de plantilla: template<typename K, typename V, template<typename> typename C = my_array> class Map { C<K> key; C<V> value; };
Resolución de nombres para parámetros de plantilla
El nombre de un parámetro de plantilla no puede ser redeclarado dentro de su ámbito (incluyendo ámbitos anidados). No se permite que un parámetro de plantilla tenga el mismo nombre que el nombre de la plantilla.
template<class T, int N> class Y { int T; // error: parámetro de plantilla redeclarado void f() { char T; // error: parámetro de plantilla redeclarado } }; template<class X> class X; // error: parámetro de plantilla redeclarado
En la definición de un miembro de una plantilla de clase que aparece fuera de la definición de la plantilla de clase, el nombre de un miembro de la plantilla de clase oculta el nombre de un parámetro de plantilla de cualquier plantilla de clase envolvente, pero no un parámetro de plantilla del miembro si el miembro es una plantilla de clase o función.
template<class T> struct A { struct B {}; typedef void C; void f(); template<class U> void g(U); }; template<class B> void A<B>::f() { B b; // B de A, no el parámetro de plantilla } template<class B> template<class C> void A<B>::g(C) { B b; // B de A, no el parámetro de plantilla C c; // el parámetro de plantilla C, no C de A }
En la definición de un miembro de una class template que aparece fuera del namespace que contiene la definición de la class template, el nombre de un template parameter oculta el nombre de un miembro de este namespace.
namespace N { class C {}; template<class T> class B { void f(T); }; } template<class C> void N::B<C>::f(C) { C b; // C es el parámetro de plantilla, no N::C }
En la definición de una plantilla de clase o en la definición de un miembro de dicha plantilla que aparece fuera de la definición de la plantilla, para cada clase base no- dependiente , si el nombre de la clase base o el nombre de un miembro de la clase base es el mismo que el nombre de un parámetro de plantilla, el nombre de la clase base o el nombre del miembro oculta el nombre del parámetro de plantilla.
struct A { struct B {}; int C; int Y; }; template<class B, class C> struct X : A { B b; // B de A C b; // error: C de A no es un nombre de tipo };
Argumentos de plantilla predeterminados
Los argumentos predeterminados de plantilla se especifican en las listas de parámetros después del = signo. Se pueden especificar valores predeterminados para cualquier tipo de parámetro de plantilla (tipo, constante o plantilla) , pero no para paquetes de parámetros (since C++11) .
Si se especifica un valor predeterminado para un parámetro de plantilla de una plantilla de clase primaria , plantilla de variable primaria, (desde C++14) o plantilla de alias, cada parámetro de plantilla subsiguiente debe tener un argumento predeterminado , excepto que el último puede ser un paquete de parámetros de plantilla (desde C++11) . En una plantilla de función, no existen restricciones sobre los parámetros que siguen a un predeterminado , y un paquete de parámetros puede ser seguido por más parámetros de tipo solo si tienen valores predeterminados o pueden deducirse de los argumentos de la función (desde C++11) .
Los parámetros predeterminados no están permitidos
- en la definición fuera de la clase de un miembro de una class template (deben proporcionarse en la declaración dentro del cuerpo de la clase). Nótese que las member templates de clases no template pueden usar parámetros por defecto en sus definiciones fuera de la clase (ver GCC bug 53856 )
- en friend class template declarations
|
(hasta C++11) |
|
En una declaración de función amiga plantilla, los argumentos de plantilla predeterminados están permitidos solo si la declaración es una definición, y no aparecen otras declaraciones de esta función en esta unidad de traducción. |
(since C++11) |
Los argumentos de plantilla predeterminados que aparecen en las declaraciones se fusionan de manera similar a los argumentos de función predeterminados:
template<typename T1, typename T2 = int> class A; template<typename T1 = int, typename T2> class A; // lo anterior es equivalente a lo siguiente: template<typename T1 = int, typename T2 = int> class A;
Pero el mismo parámetro no puede recibir argumentos predeterminados dos veces en el mismo ámbito:
template<typename T = int> class X; template<typename T = int> class X {}; // error
Al analizar un argumento de plantilla predeterminado para un parámetro de plantilla constante, el primer > no anidado se toma como el final de la lista de parámetros de plantilla en lugar de un operador mayor-que:
template<int i = 3 > 4> // error de sintaxis class X { /* ... */ }; template<int i = (3 > 4)> // Correcto class Y { /* ... */ };
Las listas de parámetros de plantilla de los parámetros de plantilla de plantilla pueden tener sus propios argumentos predeterminados, los cuales solo están en efecto donde el propio parámetro de plantilla de plantilla está en alcance:
// plantilla de clase, con un parámetro de plantilla de tipo con un valor predeterminado template<typename T = float> struct B {}; // el parámetro de plantilla T tiene una lista de parámetros, que // consiste en un parámetro de plantilla de tipo con un valor predeterminado template<template<typename = float> typename T> struct A { void f(); void g(); }; // definiciones de plantillas de funciones miembro fuera del cuerpo template<template<typename TT> class T> void A<T>::f() { T<> t; // error: TT no tiene valor predeterminado en el ámbito } template<template<typename TT = char> class T> void A<T>::g() { T<> t; // OK: t es T<char> }
Acceso a miembros para los nombres utilizados en un parámetro de plantilla predeterminado se verifica en la declaración, no en el punto de uso:
class B {}; template<typename T> class C { protected: typedef T TT; }; template<typename U, typename V = typename U::TT> class D: public U {}; D<C<B>>* d; // error: C::TT es protegido
|
El argumento de plantilla predeterminado se instancia implícitamente cuando se necesita el valor de ese argumento predeterminado, excepto si la plantilla se utiliza para nombrar una función: template<typename T, typename U = int> struct S {}; S<bool>* p; // El argumento predeterminado para U se instancia en este punto // el tipo de p es S<bool, int>* |
(desde C++14) |
Notas
Antes de C++26, los parámetros de plantilla constantes se denominaban parámetros de plantilla no tipo en la terminología estándar. La terminología fue cambiada por P2841R6 / PR#7587 .
|
En parámetros de plantilla, las restricciones de tipo pueden utilizarse tanto para parámetros de tipo como constantes, dependiendo de si auto está presente. template<typename> concept C = true; template<C, // type parameter C auto // constant parameter > struct S{}; S<int, 0> s;
|
(desde C++20) |
| Macro de prueba de características | Valor | Estándar | Característica |
|---|---|---|---|
__cpp_nontype_template_parameter_auto
|
201606L
|
(C++17) | Declarar parámetros de plantilla constantes con auto |
__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 |
Ejemplos
#include <array> #include <iostream> #include <numeric> // parámetro de plantilla constante simple template<int N> struct S { int a[N]; }; template<const char*> struct S2 {}; // ejemplo constante complejo template < char c, // tipo integral int (&ra)[5], // referencia lvalue a objeto (de tipo array) int (*pf)(int), // puntero a función int (S<10>::*a)[10] // puntero a miembro de objeto (de tipo int[10]) > struct Complicated { // llama a la función seleccionada en tiempo de compilación // y almacena el resultado en el array seleccionado en tiempo de compilación void foo(char base) { ra[4] = pf(c - base); } }; // S2<"fail"> s2; // error: literal de cadena no puede usarse char okay[] = "okay"; // objeto estático con vinculación // S2<&okay[0]> s3; // error: elemento de array no tiene vinculación S2<okay> s4; // funciona int a[5]; int f(int n) { return n; } // C++20: NTTP puede ser un tipo de clase literal template<std::array arr> constexpr auto sum() { return std::accumulate(arr.cbegin(), arr.cend(), 0); } // C++20: los argumentos de plantilla de clase se deducen en el sitio de llamada static_assert(sum<std::array<double, 8>{3, 1, 4, 1, 5, 9, 2, 6}>() == 31.0); // C++20: deducción de argumentos NTTP y CTAD static_assert(sum<std::array{2, 7, 1, 8, 2, 8}>() == 28); int main() { S<10> s; // s.a es un array de 10 int s.a[9] = 4; Complicated<'2', a, f, &S<10>::a> c; c.foo('0'); std::cout << s.a[9] << a[4] << '\n'; }
Salida:
42
|
Esta sección está incompleta
Razón: más ejemplos |
Informes de defectos
Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares de C++ publicados anteriormente.
| DR | Aplicado a | Comportamiento publicado | Comportamiento correcto |
|---|---|---|---|
| CWG 184 | C++98 |
no se especificaba si los parámetros de plantilla de los parámetros
de plantilla de plantilla pueden tener argumentos predeterminados |
se añadió especificación |
| CWG 1922 | C++98 |
no estaba claro si una plantilla de clase cuyo nombre es un
nombre de clase inyectado puede usar los argumentos predeterminados en declaraciones previas |
permitido |
| CWG 2032 | C++14 |
para plantillas de variables, no había restricción en los parámetros
de plantilla después de un parámetro de plantilla con un argumento predeterminado |
aplicar la misma restricción
que en plantillas de clase y plantillas de alias |
| CWG 2542 | C++20 | no estaba claro si el tipo de clausura es estructural | no es estructural |
| CWG 2845 | C++20 | el tipo de clausura no era estructural |
es estructural
si no tiene capturas |