Partial template specialization
Permite personalizar plantillas de clase y variable (desde C++14) para una categoría determinada de argumentos de plantilla.
Contenidos |
Sintaxis
template
<
lista-de-parámetros
>
clave-de-clase
nombre-de-cabecera-de-clase
<
lista-de-argumentos
>
declaración
|
(1) | ||||||||
template
<
lista-de-parámetros
>
secuencia-de-especificadores-de-declaración
declarador
<
lista-de-argumentos
>
inicializador
(opcional)
|
(2) | (since C++14) | |||||||
donde class-head-name identifica el nombre de una plantilla de clase previamente declarada y declarator identifica el nombre de una plantilla de variable previamente declarada (desde C++14) .
La especialización parcial puede declararse en cualquier ámbito donde su plantilla principal pueda definirse (lo cual puede ser diferente del ámbito donde se define la plantilla principal; como con la especialización fuera de clase de una plantilla de miembro ). La especialización parcial debe aparecer después de la declaración de la plantilla no especializada.
Por ejemplo,
template<class T1, class T2, int I> class A {}; // plantilla principal template<class T, int I> class A<T, T*, I> {}; // #1: especialización parcial donde T2 es un puntero a T1 template<class T, class T2, int I> class A<T*, T2, I> {}; // #2: especialización parcial donde T1 es un puntero template<class T> class A<int, T*, 5> {}; // #3: especialización parcial donde // T1 es int, I es 5, y T2 es un puntero template<class X, class T, int I> class A<X, T*, I> {}; // #4: especialización parcial donde T2 es un puntero
Ejemplos de especializaciones parciales en la biblioteca estándar incluyen std::unique_ptr , que tiene una especialización parcial para tipos de array.
La lista de argumentos
Las siguientes restricciones se aplican a la argument-list de una especialización parcial de plantilla:
template<class T1, class T2, int I> class B {}; // primary template template<class X, class Y, int N> class B<X, Y, N> {}; // error
|
Además, la especialización debe ser más especializada que la plantilla primaria template<int N, typename T1, typename... Ts> struct B; template<typename... Ts> struct B<0, Ts...> {}; // Error: not more specialized |
(desde C++11) |
template<int I, int J> struct A {}; template<int I> struct A<I + 5, I * 2> {}; // error, I is not deducible template<int I, int J, int K> struct B {}; template<int I> struct B<I, I * 2, 2> {}; // OK: first parameter is deducible
template<class T, T t> struct C {}; // primary template template<class T> struct C<T, 1>; // error: type of the argument 1 is T, // which depends on the parameter T template<int X, int (*array_ptr)[X]> class B {}; // primary template int array[5]; template<int X> class B<X, &array> {}; // error: type of the argument &array is // int(*)[X], which depends on the parameter X
Búsqueda de nombres
Las especializaciones parciales de plantillas no se encuentran mediante búsqueda por nombre. Solo si la plantilla principal se encuentra mediante búsqueda por nombre, se consideran sus especializaciones parciales. En particular, una declaración using que hace visible una plantilla principal, también hace visibles las especializaciones parciales:
namespace N { template<class T1, class T2> class Z {}; // plantilla primaria } using N::Z; // hace referencia a la plantilla primaria namespace N { template<class T> class Z<T, T*> {}; // especialización parcial } Z<int, int*> z; // la búsqueda de nombre encuentra N::Z (la plantilla primaria), // luego se utiliza la especialización parcial con T = int
Ordenamiento parcial
Cuando una clase o variable (since C++14) template se instancia, y hay especializaciones parciales disponibles, el compilador debe decidir si se va a utilizar la plantilla principal o una de sus especializaciones parciales.
// given the template A as defined above A<int, int, 1> a1; // no specializations match, uses primary template A<int, int*, 1> a2; // uses partial specialization #1 (T = int, I = 1) A<int, char*, 5> a3; // uses partial specialization #3, (T = char) A<int, char*, 1> a4; // uses partial specialization #4, (X = int, T = char, I = 1) A<int*, int*, 2> a5; // error: matches #2 (T = int, T2 = int*, I= 2) // matches #4 (X = int*, T = int, I = 2) // neither one is more specialized than the other
Informalmente, "A está más especializado que B" significa "A acepta un subconjunto de los tipos que B acepta".
Formalmente, para establecer la relación de más-especializada-que entre especializaciones parciales, cada una se convierte primero en una plantilla de función ficticia de la siguiente manera:
- la primera plantilla de función tiene los mismos parámetros de plantilla que la primera especialización parcial y tiene solo un parámetro de función, cuyo tipo es una especialización de plantilla de clase con todos los argumentos de plantilla de la primera especialización parcial
- la segunda plantilla de función tiene los mismos parámetros de plantilla que la segunda especialización parcial y tiene solo un parámetro de función cuyo tipo es una especialización de plantilla de clase con todos los argumentos de plantilla de la segunda especialización parcial.
Las plantillas de función se clasifican como si fuera para function template overloading .
template<int I, int J, class T> struct X {}; // plantilla principal template<int I, int J> struct X<I, J, int> { static const int s = 1; }; // especialización parcial #1 // plantilla de función ficticia para #1 es // template<int I, int J> void f(X<I, J, int>); #A template<int I> struct X<I, I, int> { static const int s = 2; }; // especialización parcial #2 // plantilla de función ficticia para #2 es // template<int I> void f(X<I, I, int>); #B int main() { X<2, 2, int> x; // tanto #1 como #2 coinciden // ordenamiento parcial para plantillas de función: // #A desde #B: void(X<I, J, int>) desde void(X<U1, U1, int>): deducción OK // #B desde #A: void(X<I, I, int>) desde void(X<U1, U2, int>): deducción falla // #B es más especializada // #2 es la especialización que se instancia std::cout << x.s << '\n'; // imprime 2 }
Miembros de especializaciones parciales
La lista de parámetros de plantilla y la lista de argumentos de plantilla de un miembro de una especialización parcial deben coincidir con la lista de parámetros y la lista de argumentos de la especialización parcial.
Al igual que con los miembros de las plantillas primarias, solo necesitan definirse si se utilizan en el programa.
Los miembros de las especializaciones parciales no están relacionados con los miembros de la plantilla principal.
La especialización explícita (completa) de un miembro de una especialización parcial se declara de la misma manera que una especialización explícita de la plantilla principal.
template<class T, int I> // plantilla principal struct A { void f(); // declaración de miembro }; template<class T, int I> void A<T, I>::f() {} // definición de miembro de plantilla principal // especialización parcial template<class T> struct A<T, 2> { void f(); void g(); void h(); }; // miembro de especialización parcial template<class T> void A<T, 2>::g() {} // especialización explícita (completa) // de un miembro de especialización parcial template<> void A<char, 2>::h() {} int main() { A<char, 0> a0; A<char, 2> a2; a0.f(); // OK, usa la definición de miembro de la plantilla principal a2.g(); // OK, usa la definición de miembro de la especialización parcial a2.h(); // OK, usa la definición completamente especializada de // el miembro de una especialización parcial a2.f(); // error: no hay definición de f() en la // especialización parcial A<T,2> (no se usa la plantilla principal) }
Si una plantilla primaria es miembro de otra plantilla de clase, sus especializaciones parciales son miembros de la plantilla de clase envolvente. Si la plantilla envolvente se instancia, la declaración de cada especialización parcial miembro también se instancia (de la misma manera que las declaraciones, pero no las definiciones, de todos los demás miembros de una plantilla se instancian).
Si la plantilla de miembro principal está explícitamente (completamente) especializada para una especialización (implícita) dada de la plantilla de clase envolvente, las especializaciones parciales de la plantilla de miembro se ignoran para esta especialización de la plantilla de clase envolvente.
Si una especialización parcial de la plantilla de miembro está explícitamente especializada para una especialización (implícita) dada de la plantilla de clase envolvente, la plantilla de miembro primaria y sus otras especializaciones parciales aún se consideran para esta especialización de la plantilla de clase envolvente.
template<class T> struct A // plantilla de clase envolvente { template<class T2> struct B {}; // plantilla de miembro primaria template<class T2> struct B<T2*> {}; // especialización parcial de plantilla de miembro }; template<> template<class T2> struct A<short>::B {}; // especialización completa de plantilla de miembro primaria // (ignorará la parcial) A<char>::B<int*> abcip; // usa especialización parcial T2=int A<short>::B<int*> absip; // usa especialización completa de la primaria (ignora parcial) A<char>::B<int> abci; // usa primaria
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 727 | C++98 |
especializaciones parciales y completas no permitidas en
ámbito de clase |
permitidas en cualquier ámbito |
| CWG 1315 | C++98 |
el parámetro de plantilla no podía usarse en argumentos
constantes de plantilla excepto en expresiones-id |
expresiones válidas mientras sean deducibles |
| CWG 1495 | C++11 | la especificación no era clara al involucrar paquetes de parámetros | la especialización debe ser más especializada |
| CWG 1711 | C++14 | falta de especificación para especializaciones parciales de plantillas de variables | añadir soporte para plantillas de variables |
| CWG 1819 | C++98 | ámbitos aceptables para la definición de especialización parcial |
permitir que la especialización parcial pueda declararse
en el mismo ámbito que las plantillas primarias |
| CWG 2330 | C++14 | faltaban referencias a plantillas de variables | añadir soporte para plantillas de variables |