Namespaces
Variants

Template parameters

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

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:
  • un tipo estructural (ver abajo)
(desde C++17)
(desde C++20)
name - el nombre del parámetro de plantilla constante
default - el default template argument
1) Un parámetro de plantilla constante.
2) Un parámetro de plantilla constante con un argumento de plantilla predeterminado.
3) Un paquete de parámetros de plantilla constante parameter pack .


Un tipo estructural es uno de los siguientes tipos (opcionalmente calificado con cv, las calificaciones se ignoran):

(desde C++11)
  • todas las clases base y miembros de datos no estáticos son públicos y no mutables, y
  • los tipos de todas las clases base y miembros de datos no estáticos son tipos estructurales o arreglos (posiblemente multidimensionales) de estos.
(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 T denota un objeto de duración de almacenamiento estático de tipo const T , llamado objeto de parámetro de plantilla , que es equivalente como argumento de plantilla al argumento de plantilla correspondiente después de ser convertido al tipo del parámetro de plantilla. No existen dos objetos de parámetro de plantilla que sean equivalentes como argumentos de plantilla.

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
1) Un parámetro de plantilla de tipo sin valor predeterminado.
template<class T>
class My_vector { /* ... */ };
2) Un parámetro de plantilla de tipo con un valor predeterminado.
template<class T = void>
struct My_op_functor { /* ... */ };
3) Un paquete de parámetros de plantilla de tipo parameter pack .
template<typename... Ts>
class My_tuple { /* ... */ };
4) Un parámetro de plantilla de tipo restringido sin un valor predeterminado.
template<My_concept T>
class My_constrained_vector { /* ... */ };
5) Un parámetro de plantilla de tipo restringido con un valor predeterminado.
template<My_concept T = void>
class My_constrained_op_functor { /* ... */ };
6) Una plantilla de tipo restringido parameter pack .
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 P cuyo type-constraint es Q designando el concepto C introduce una constraint-expression E de acuerdo con las siguientes reglas:

  • si Q es C (sin lista de argumentos),
  • si P no es un parameter pack, E es simplemente C<P>
  • en caso contrario, P es un parameter pack, E es una fold-expression (C<P> && ...)
  • si Q es C<A1,A2...,AN> , entonces E es C<P,A1,A2,...AN> o (C<P,A1,A2,...AN> && ...) , respectivamente.
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)
1) Un parámetro de plantilla de plantilla con un nombre opcional.
2) Un parámetro de plantilla de plantilla con un nombre opcional y un valor predeterminado.
3) Un paquete de parámetros de plantilla de plantilla parameter pack con un nombre opcional.


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

(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

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