Espacios de nombres
Variantes
Acciones

Parámetros de plantilla y argumentos de plantilla

De cppreference.com
< cpp‎ | language

Contenido

[editar] Parámetros de plantilla

Cada plantilla está parametrizada por uno o más parámetros de plantilla, indicado en la lista-de-parámetros de la sintaxis de declaración de plantilla:

template < lista-de-parámetros > declaración

Cada parámetro en la lista-de-parámetros puede ser:

  • un parámetro de plantilla de no tipo;
  • un parámetro de plantilla de tipo;
  • un parámetro de plantilla de plantilla.

[editar] Parámetro de plantilla de no tipo

tipo nombre(opcional) (1)
tipo nombre(opcional) = default (2)
tipo ... nombre(opcional) (3) (desde C++11)
marcador-de-posición nombre (4) (desde C++17)
1) Un parámetro de plantilla de no tipo con un nombre opcional.
2) Un parámetro de plantilla de no tipo con un nombre opcional y un valor por defecto.
3) Un paquete de parámetros de plantilla de no tipo con un nombre opcional.
4) Un parámetro de plantilla de no tipo con un tipo de marcador de posición. El marcador-de-posición puede ser cualquier tipo que incluya el marcador de posición auto (tales como el auto sencillo, auto ** o auto &), un marcador de posición para un tipo clase deducido (desde C++20), o decltype(auto).

Un parámetro de plantilla de no tipo debe tener un tipo estructural, que es uno de los siguientes tipos (opcionalmente calificado-cv, los calificadores se ignoran):

(desde C++11)
  • todas las clases base y datos miembro no estáticos son públicos y no mutables y,
  • los tipos de todas las clases base y miembros dato no estáticos son tipos estructurales o (que pueden ser multidimensionales) un array de los mismos.
(desde C++20)

Pueden escribirse los tipos array y función en la declaración de la plantilla, pero se reemplazan automáticamente por punteros a objeto y punteros a función, según lo apropiado.

Cuando el nombre de un parámetro de plantilla de no tipo 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 referencia lvalue, o a menos que su tipo sea un tipo clase (desde C++20).

Un parámetro de plantilla de la forma class Foo no es un parámetro de plantilla de no tipo no denominado de tipo Foo, aun si class Foo es un especificador de tipo elaborado y class Foo x; declara que x sea de tipo Foo.

El tipo de un parámetro de plantilla de no tipo puede deducirse si incluye un tipo marcador de posición (auto, un marcador de posición para un tipo clase deducido (desde C++20), o decltype(auto)). La deducción se lleva a cabo como si se dedujera el tipo de la variable x en la declaración inventada T x = argumento-de-plantilla;, donde T es el tipo declarado del parámetro de plantilla. Si el tipo deducido no se permite para un parámetro de plantilla de no tipo, el programa está mal formado.

template<auto n> 
struct B { /* ... */ };
 
B<5> b1;   // de acuerdo: el tipo del parámetro de plantilla de no tipo es int
B<'a'> b2; // de acuerdo: el tipo del parámetro de plantilla de no tipo es char
B<2.5> b3; // ERROR (hasta C++20): el tipo del parámetro de plantilla de no tipo no puede 
           //                      ser double
 
// Marcador de posición de tipo de clase deducida C++20, los argumentos de la plantilla de 
// clase se deducen en el lugar de la llamada
template<std::array arr>
void f();
 
f<std::array<double, 8>{}>();

Para los paquetes de parámetros de plantilla de no tipo cuyos tipos utilizan un tipo marcador de posición, se deduce el tipo independientemente para cada argumento de plantilla y no necesitan coincidir:

template<auto...> 
struct C {};
 
C<'C', 0, 2L, nullptr> x; // de acuerdo
(desde C++17)

Un identificador que denomina a un parámetro de plantilla de no tipo de tipo clase T denota a un objeto de una duración de almacenamiento estática de tipo const T, llamado un objeto de parámetro de plantilla, cuyo valor es el del argumento de plantilla correspondiente después que se ha convertido al tipo del parámetro de plantilla. Todos estos parámetros de plantilla en el programa del mismo tipo con el mismo valor denotan el mismo objeto de parámetro de plantilla. Un objeto de parámetro de plantilla deberá tener una destrucción constante.

struct A 
{ 
    friend bool operator==(const A&, const A&) = default; 
};
 
template<A a> 
void f() 
{
    &a; // de acuerdo
    const A& ra = a, &rb = a; // Ambos están vinculados al mismo objeto de parámetro 
                              // de plantilla
    assert(&ra == &rb); // pasa
}
(desde C++20)

[editar] Parámetro de plantilla de tipo

clave-de-parámetro-de-tipo nombre(opcional) (1)
clave-de-parámetro-de-tipo nombre(opcional) = default (2)
clave-de-parámetro-de-tipo ... nombre(opcional) (3) (desde C++11)
restricción-de-tipo nombre(opcional) (4) (desde C++20)
restricción-de-tipo nombre(opcional) = default (5) (desde C++20)
restricción-de-tipo ... nombre(opcional) (6) (desde C++20)
{{par | clave-de-parámetro-de-tipo| Es ya sea typename o class. No existe diferencia entre estas palabras clave en una declaración de parámetro de plantilla de tipo.
restricción-de-tipo - Ya sea el nombre de un concepto o el nombre de un concepto seguido de una lista de argumentos de plantilla (entre corchetes angulares). De cualquier forma, el nombre del concepto opcionalmente puede calificarse.


1) Un parámetro de plantilla de tipo sin un valor por defecto.
template<class T>
class Mi_vector { /* ... */ };
2) Un parámetro de plantilla de tipo con un valor por defecto.
template<class T = void>
struct Mi_op_functor { /* ... */ };
3) Un paquete de parámetros de plantilla de tipo.
template<typename... Ts>
class Mi_tupla { /* ... */ };
4) Un parámetro de plantilla de tipo restringido sin un valor predeterminado.
template<Mi_concepto T>
class Mi_vector_restringido { /* ... */ };
5) Un parámetro de plantilla de tipo restringido con un valor predeterminado.
template<Mi_concepto T = void>
class Mi_op_functor_restringido { /* ... */ };
6) Un paquete de parámetros de plantilla de tipo restringido.
template<Mi_concepto... Ts>
class Mi_tupla_restringida { /* ... */ };

El nombre del parámetro es opcional:

// Declaraciones de las plantillas mostradas arriba:
template<class> 
class Mi_vector;
template<class = void> 
struct Mi_op_functor;
template<typename...> 
class Mi_tupla;

En el cuerpo de la declaración de plantilla, el nombre de un parámetro de tipo es un nombre typedef que es un alias del tipo proporcionado cuando se instancia la plantilla.

Cada parámetro restringido P cuya restricción-de-tipo es Q que designa el concepto C introduce una expresión de restricción E de acuerdo con las siguientes reglas:

  • si Q es C (sin una lista de argumentos),
  • si P no es un paquete de parámetros, E simplemente es C<P>
  • de lo contrario, P es un paquete de parámetros, E es una expresión de pliegue (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> // concepto variádico
concept C2 = true; 
template<typename T, typename U> 
concept C3 = true;
 
template<C1 T> struct s1;         // la expresión de restricción es C1<T>
template<C1... T> struct s2;      // la expresión de restricción es (C1<T> && ...)
template<C2... T> struct s3;      // la expresión de restricción es (C2<T> && ...)
template<C3<int> T> struct s4;    // la expresión de restricción es C3<T, int>
template<C3<int>... T> struct s5; // la expresión de restricción es (C3<T, int> && ...)
(desde C++20)

[editar] Parámetro de plantilla de plantilla

template < lista-de-parámetros > clave-de-parámetro-de-tipo nombre(opcional) (1)
template < lista-de-parámetros > clave-de-parámetro-de-tipo nombre(opcional) = default (2)
template < lista-de-parámetros > clave-de-parámetro-de-tipo ... nombre(opcional) (3) (desde C++11)
clave-de-parámetro-de-tipo - 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 por defecto.
3) Un paquete de parámetros de plantilla de plantilla con un nombre opcional.

En el cuerpo de la declaración de la plantilla, el nombre de este parámetro es un nombre-de-plantilla (y necesita que se instancien los argumentos).

template<typename T> 
class mi_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 = mi_array>
class Mapa
{
    C<K> clave;
    C<V> valor;
};

[editar] Resolución de nombres para parámetros de plantilla

No se permite que se redeclare el nombre de un parámetro de plantilla 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 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 circundante, pero no un parámetro de plantilla del miembro si el miembro es una plantilla de clase o plantilla de 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;            // la B de A, no el parámetro de plantilla
}
 
template<class B>
template<class C>
void A<B>::g(C)
{
    B b;            // la B de A, no el parámetro de plantilla
    C c;            // el parámetro de plantilla C, no la C de A
}

En la definición de un miembro de una plantilla de clase que aparece fuera del espacio de nombres que contiene la definición de la plantilla de clase, el nombre de un parámetro de plantilla oculta el nombre de un miembro de este espacio de nombres.

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 tal 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;            // la B de A
    C b;            // ERROR: la C de A no es un nombre de tipo.
};

[editar] Argumentos de plantilla

Para que una plantilla pueda instanciarse, cada parámetro de plantilla (de tipo, de no tipo, o de plantilla) debe reemplazarse por un argumento de plantilla correspondiente. Para las plantillas de clase, los argumentos son o bien proporcionados explícitamente, deducidos del inicializador, (desde C++17) o por defecto. Para las plantillas de función, los argumentos se proporcionan explícitamente, deducidos del contexto, o por defecto.

Si un argumento puede interpretarse tanto como un id-de-tipo como una expresión, siempre se interpreta como un id-de-tipo, incluso si el parámetro de plantilla correspondiente es de no tipo:

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
}

[editar] Argumentos de plantilla de no tipo

Se aplican las siguientes limitaciones al instanciar plantillas que tengan par��metros de plantilla de no tipo:

  • Para tipos enteros y aritméticos, el argumento de plantilla proporcionado durante la instanciación deberá ser una expresión constante convertida del tipo del parámetro de plantilla (por lo que se aplica cierta conversión implícita).
  • Para punteros a objeto, los argumentos de plantilla tienen que designar la dirección de un objeto completo con duración de almacenamiento estática y un enlace (ya sea interno o externo), o una expresión constante que se evalúe al puntero nulo apropriado o al valor std::nullptr_t (desde C++11).
  • Para punteros a funciones, los argumentos válidos son punteros a funciones con enlace (o expresiones constantes que se evalúen a valores de puntero nulo).
  • Para parámetros referencia lvalue, el argumento proporcionado en la instanciación no puede ser un temporal, un lvalue sin nombre, o un lvalue denominado sin enlace (en otras palabras, el argumento debe tener enlace).
  • Para punteros a miembro, el argumento debe ser un puntero a miembro expresado como &Clase::Miembro o una expresión constante que se evalúe al puntero nulo o al valor std::nullptr_t (desde C++11).

En particular, esto implica que los literales de cadena, las direcciones a elementos de arrays, y direcciones de miembros no estáticos no pueden usarse como argumentos de plantilla para instanciar plantillas cuyos parámetros de plantilla de no tipo correspondientes son punteros a objetos.

(hasta C++17)

El argumento de plantilla que puede usarse con un parámetro de plantilla de no tipo puede ser cualquier expresión constante convertida del tipo del parámetro de plantilla.

template<const int* pci> 
struct X {};
 
int ai[10];
X<ai> xi;  // de acuerdo: 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;  // de acuerdo: no hay conversión
 
template<int (&pa)[5]> 
struct W {};
 
int b[5];
W<b> w; // de acuerdo: no hay conversión
 
void f(char);
void f(int);
 
template<void (*pf)(int)> 
struct A {};
 
A<&f> a; // de acuerdo: la resolución de sobrecarga selecciona a f(int)

Las únicas excepciones son que los parámetros de no tipo de tipo referencia o puntero y los datos miembro no estáticos de tipo referencia o puntero en un parámetro de plantilla de no tipo de tipo clase y sus subobjetos (desde C++20) no pueden referirse a, o ser la dirección de

  • un objeto temporal (incluyendo uno creado durante la inicialización de referencia);
  • un literal de cadena;
  • el resultado de typeid;
  • la variable predefinida __func__;
  • o un subobjeto (incluyendo un miembro de clase no estático, subobjeto base, o elemento de array) de uno de los anteriores (desde C++20);
template<class T, const char* p> 
class X {};
 
X<int, "Saavedra"> 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;   // de acuerdo: dirección de miembro estático
X<&S::s> x6;  // de acuerdo: dirección de miembro estático
 
template<const int& CRI> s
truct B {};
 
B<1> b2;     // ERROR: se requeriría temporal para argumento de plantilla
int c = 1;
B<c> b1;     // de acuerdo
(desde C++17)

[editar] Argumentos de plantilla de tipo

Un argumento de plantilla para un parámetro de plantilla de tipo debe ser un id-de-tipo, que puede denominar un tipo incompleto:

template<typename T> 
class X {}; // plantilla de clase
 
struct A; // tipo incompleto
typedef struct {} B; // alias de tipo a un tipo sin denominar
 
int main()
{
    X<A> x1; // de acuerdo: 'A' denomina un tipo
    X<A*> x2; // de acuerdo: 'A*' denomina un tipo
    X<B> x3; // de acuerdo: 'B' denomina un tipo
}

[editar] Argumentos de plantilla de plantilla

Un argumento de plantilla para un parámetro de plantilla de plantilla debe ser una expresión-id que denomine a una plantilla de clase o a un alias de plantilla.

Cuando el argumento es una plantilla de clase, solamente se considera la plantilla primaria cuando coincide con el parámetro. Se consideran las especializaciones parciales, si es que las hay, solamente cuando sucede que una especialización basada en este parámetro de plantilla de plantilla es instanciada.

template<typename T> 
class A { int x; }; // plantilla primaria
 
template<class T> 
class A<T*> { long x; }; // especialización parcial
 
// plantilla de clase con un parámetro de plantilla de plantilla V
template<template<typename> class V> 
class C
{
    V<int> y; // usa la plantilla primaria
    V<int*> z; // usa la especialización parcial
};
 
C<A> c; // c.y.x tiene tipo int, c.z.x tiene tipo long

Para coincidir un argumento de plantilla de plantilla A con el parámetro de plantilla de plantilla P, P debe estar especializado al menos tanto 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 se corresponden con é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 en 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 A (incluyendo los argumentos por defecto):

  • Cada una de las dos plantillas de funciones tiene los mismos parámetros de plantilla, respectivamente, que P o A.
  • Cada plantilla de función tiene un único parámetro de función cuyo tipo es una especialización de X con argumentos de plantilla correspondientes a los parámetros de plantilla de la respectiva plantilla de función donde, para cada parámetro de plantilla PP en la lista de parámetros de plantilla de la plantilla de función, un argumento de plantilla correspondiente AA se forma. Si PP declara un paquete de parámetros, entonces AA es la expansión del paquete PP...; de lo contrario, (desde C++11) AA es la expresión-id PP.

Si la reescritura produce un tipo inválido, entonces P no es al menos tan especializado como A.

template<typename T> 
struct eval; // plantilla primaria 
 
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; // de acuerdo: coincide la especialización parcial de eval
eval<B<int, float>> eB; // de acuerdo: coincide la especialización parcial de eval
eval<C<17>> eC; // ERROR: C no coincide 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 denomina 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 denomina 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 de no tipo

Antes de la adopción de P0522R0, cada uno de los parámetros de plantilla de A debe coincidir exactamente con los parámetros de plantilla de P. Esto impide la aceptación de muchos argumentos de plantilla razonables.

Aunque se señalo muy temprano (CWG#150), cuando se resolvió, los cambios se aplicaron al documentos 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 deshabilita en todos los modos del lenguaje anteriores a C++17 de forma predeterminada, solo se puede habilitar configurando un flag de compilador en estos modos.
  • Clang lo deshabilita en todos los modos del lenguaje de forma predeterminada, solo se puede habilitar configurando un flag de compilador.
  • Microsoft Visual Studio lo trata como una característica normal de C++17 y solo lo habilita en C++17 y posteriores (es decir, no soportado 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; // de acuerdo
X<B> xb; // de acuerdo después de P0522R0
         // ERROR previamente: no es una coincidencia exacta
X<C> xc; // de acuerdo después de P0522R0
         // ERROR previamente: no es una coincidencia exacta
 
template<template<class ...> class Q> class Y { /* ... */ };
Y<A> ya; // de acuerdo
Y<B> yb; // de acuerdo
Y<C> yc; // de acuerdo
 
template<auto n> class D { /* ... */ }; // observación: C++17
template<template<int> class R> class Z { /* ... */ };
Z<D> zd; // de acuerdo después de P0522R0: el parámetro de plantilla
             // es más especializado que el argumento de plantilla
 
template <int> struct SI { /* ... */ };
template <template <auto> class> void FA();  // observación: C++17
FA<SI>();  // ERROR


[editar] Argumentos de plantilla por defecto

Los argumentos de plantilla por defecto se especifican en las listas de parámetros después del signo =. Pueden especificarse valores por defecto para cualquier tipo de parámetro de plantilla (de tipo, de no tipo, o de plantilla), pero no para los paquetes de parámetros (desde C++11).

Si se especifica el valor por defecto 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 subsecuente debe tener un argumento por defecto, 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 en los parámetros que puedan seguir a un valor por defecto, y un paquete de parámetros puede ser seguido de más parámetros de tipo solamente si tienen valores por defecto o pueden deducirse de los argumentos de función (desde C++11).

No se permiten los parámetros por defecto

(hasta C++11)

En una declaración de una plantilla de función amiga, se permiten los argumentos de plantilla por defecto solamente si la declaración es una definición, y no aparece ninguna otra declaración en esta unidad de traducción.

(desde C++11)

Los argumentos de plantilla por defecto que aparecen en las declaraciones se combinan de manera similar a los argumentos de función por defecto:

template<typename T1, typename T2 = int> class A;
template<typename T1 = int, typename T2> class A;
 
// lo anterior es lo mismo que lo siguiente:
template<typename T1 = int, typename T2 = int> class A;

Pero no se le pueden dar argumentos por defecto dos veces al mismo parámetro 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 que no es de tipo, 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 por defecto, que solamente tienen efecto donde el parámetro de plantilla de plantilla mismo se encuentra en ámbito:

// plantilla de clase, con un parámetro de plantilla de tipo con un valor por defecto
template<typename T = float> 
struct B {};
 
// el parámetro de plantilla de plantilla T tiene una lista de parámetros, que
// consiste de un parámetro de plantilla de tipo con un valor por defecto
template<template<typename = float> typename T>
 struct A
{
    void f();
    void g();
};
 
// definiciones de plantillas de función miembro fuera del cuerpo de la clase
 
template<template<typename TT> class T>
void A<T>::f()
{
    T<> t; // ERROR: TT no tiene valor por defecto en el ámbito
}
 
template<template<typename TT = char> class T>
void A<T>::g()
{
    T<> t; // de acuerdo: t es T<char>
}

El acceso a miembros para los nombres usados en un parámetro de plantilla por defecto se comprueban 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 está protegida

El argumento de plantilla por defecto se instancia implícitamente cuando el valor de ese argumento por defecto se necesita, excepto si la plantilla se usa para denominar a una función:

template<typename T, typename U = int> 
struct S { };
 
S<bool>* p; // El argumento por defecto para U se instancia en este punto
            // el tipo de p es S<bool, int>*
(desde C++14)

[editar] Equivalencia de argumentos de plantilla

La equivalencia de argumentos de plantilla se utiliza para determinar si dos id-de-plantilla son las mismas.

Dos valores son argumentos de plantilla equivalentes si son del mismo tipo y

  • son de tipo entero o enumeración y sus valores son los mismos;
  • o son de tipo puntero y tienen el mismo valor de puntero;
  • o son de tipo puntero a miembro y se refieren al mismo miembro de clase o ambos son el valor del puntero a miembro nulo;
  • o son de tipo referencia lvalue y se refieren al mismo objeto o función;
  • o son de tipo std::nullptr_t;
(desde C++11)
  • o son de tipo de punto flotante y sus valores son idénticos;
  • o son de tipo array (en cuyo caso los arrays deben ser objetos miembro de alguna clase o unión), y sus elementos correspondientes son argumentos de plantilla equivalentes;
  • o son de tipo unión y o bien ambos no tienen un miembro activo o tienen el mismo miembro activo y sus miembros activos son argumentos de plantilla equivalentes;
  • o son de tipo clase no-unión y sus subobjetos directos correspondientes y miembros referencia son argumentos de plantilla equivalentes.
(desde C++20)

[editar] Notas

Prueba de característica Valor Estándar Comentario
__cpp_nontype_template_parameter_auto 201606L (C++17) Declaración de parámetros de plantilla de no-tipo con auto
__cpp_template_template_args 201611L (c++17) Coincidencia de argumentos de plantilla de plantilla
__cpp_nontype_template_args 201411L (C++17) Permitir la evaluación constante para todos los argumentos de plantilla de no-tipo
201911L (C++20) Tipos de clase y de punto flotante en parámetros de plantilla de no-tipo

[editar] Ejemplos

#include <array>
#include <iostream>
#include <numeric>
 
// parámetro de plantilla de no tipo simple
template<int N>
struct S { int a[N]; };
 
template<const char*>
struct S2 {};
 
// ejemplo de no tipo complicado
template
<
    char c, // tipo entero
    int (&ra)[5], // referencia lvalue a un objeto (de tipo array)
    int (*pf)(int), // puntero a función
    int (S<10>::*a)[10] // puntero a objeto miembro (de tipo int[10])
> 
struct Complicado
{
    // 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<"falla"> s2; // ERROR: no puede usarse literal de cadena
char okay[] = "de acuerdo"; // objeto estático con enlace
//  S2< &okay[0] > s3; // ERROR: elemento de array no tiene enlace
S2<okay> s4; // funciona
 
int a[5];
int f(int n) { return n; }
 
// C++20: NTTP (parámetro de plantilla de no-tipo) puede ser un tipo de clase literal
template<std::array arr>
constexpr
auto suma() { return std::accumulate(arr.cbegin(), arr.cend(), 0); }
 
// C++20: se deducen los argumentos de plantilla de clase en el lugar de la llamada
static_assert(suma<std::array<double, 8>{3, 1, 4, 1, 5, 9, 2, 6}>() == 31.0);
// C++20: deducción de argumento NTTP y CTAD (deducción de argumento de plantilla de clase)
static_assert(suma<std::array{2, 7, 1, 8, 2, 8}>() == 28);
 
int main()
{
    S<10> s; // s.a es un array de 10 enteros
    s.a[9] = 4;
 
    Complicado<'2', a, f, &S<10>::a> c;
    c.foo('0');
 
    std::cout << s.a[9] << a[4] << '\n';
}

Salida:

42

[editar] Informes de defectos

Los siguientes informes de defectos de cambio de comportamiento se aplicaron de manera retroactiva a los estándares de C++ publicados anteriormente.

ID Aplicado a Comportamiento según lo publicado Comportamiento correcto
CWG 150 C++98 los argumentos de plantilla de plantilla tenían que coincidir exactamente
con las listas de parámetros de plantilla de plantilla
también se permite más especializado
(resuelto por P0522R0)
CWG 184 C++98 no se especifica si los parámetros de plantilla de los parámetros de plantilla
de plantilla pueden tener argumentos predeterminados
se añade especificación
CWG 354 C++98 los valores puntero nulo no podían ser argumentos de plantilla de no-tipo permitido
CWG 1398 C++11 los argumentos de plantilla de no tipo no podían tener el tipo
std::nullptr_t
permitido
CWG 1570 C++98 los argumentos de plantilla de no-tipo podían designar la dirección de
subobjetos
no permitido
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 anteriores
permitido
CWG 2032 C++14 para plantillas de variable, no había restricción en los parámetros de
plantilla después de un parámetros de plantilla con un argumento
predeterminado
se aplica la misma restricción que en
plantillas de clase y de alias