Explicit (full) template specialization
Permite personalizar el código de plantilla para un conjunto dado de argumentos de plantilla.
Contenidos |
Sintaxis
template <>
declaración
|
|||||||||
Cualquiera de los siguientes puede ser completamente especializado:
- plantilla de función
- plantilla de clase
- plantilla de variable (desde C++14)
- función miembro de una plantilla de clase
- miembro de datos estático de una plantilla de clase
- clase miembro de una plantilla de clase
- enumeración miembro de una plantilla de clase
- plantilla de clase miembro de una clase o plantilla de clase
- plantilla de función miembro de una clase o plantilla de clase
- plantilla de variable miembro de una clase o plantilla de clase (desde C++14)
Por ejemplo,
#include <type_traits> template<typename T> // primary template struct is_void : std::false_type {}; template<> // explicit specialization for T = void struct is_void<void> : std::true_type {}; int main() { static_assert(is_void<char>::value == false, "for any type T other than void, the class is derived from false_type"); static_assert(is_void<void>::value == true, "but when T is void, the class is derived from true_type"); }
En detalle
La especialización explícita puede declararse en cualquier ámbito donde su plantilla primaria pueda definirse (lo cual puede ser diferente del ámbito donde se define la plantilla primaria; como con la especialización fuera de clase de una plantilla de miembro ). La especialización explícita debe aparecer después de la declaración de plantilla no especializada.
namespace N { template<class T> // plantilla principal class X { /*...*/ }; template<> // especialización en el mismo espacio de nombres class X<int> { /*...*/ }; template<class T> // plantilla principal class Y { /*...*/ }; template<> // declaración anticipada de especialización para double class Y<double>; } template<> // OK: especialización en el mismo espacio de nombres class N::Y<double> { /*...*/ };
La especialización debe declararse antes del primer uso que causaría una instanciación implícita, en cada unidad de traducción donde ocurra dicho uso:
class String {}; template<class T> class Array { /*...*/ }; template<class T> // plantilla principal void sort(Array<T>& v) { /*...*/ } void f(Array<String>& v) { sort(v); // instancia implícitamente sort(Array<String>&), } // usando la plantilla principal para sort() template<> // ERROR: especialización explícita de sort(Array<String>) void sort<String>(Array<String>& v); // después de la instanciación implícita
Una especialización de plantilla que fue declarada pero no definida puede usarse igual que cualquier otro tipo incompleto (por ejemplo, se pueden usar punteros y referencias a él):
template<class T> // plantilla primaria class X; template<> // especialización (declarada, no definida) class X<int>; X<int>* p; // OK: puntero a tipo incompleto X<int> x; // error: objeto de tipo incompleto
Si una especialización explícita de una plantilla de función
o variable
(since C++14)
es
inline
/
constexpr
(since C++11)
/
constinit
/
consteval
(since C++20)
está determinado por la propia especialización explícita, independientemente de si la plantilla principal está declarada con ese especificador.
De manera similar, los
atributos
que aparecen en la declaración de una plantilla no tienen efecto en una especialización explícita de esa plantilla:
(since C++11)
template<class T> void f(T) { /* ... */ } template<> inline void f<>(int) { /* ... */ } // OK, inline template<class T> inline T g(T) { /* ... */ } template<> int g<>(int) { /* ... */ } // OK, no inline template<typename> [[noreturn]] void h([[maybe_unused]] int i); template<> void h<int>(int i) { // [[noreturn]] no tiene efecto, pero [[maybe_unused]] sí }
Especializaciones explícitas de plantillas de función
Al especializar una plantilla de función, sus argumentos de plantilla pueden omitirse si la deducción de argumentos de plantilla puede proporcionarlos a partir de los argumentos de la función:
template<class T> class Array { /*...*/ }; template<class T> // plantilla principal void sort(Array<T>& v); template<> // especialización para T = int void sort(Array<int>&); // no es necesario escribir // template<> void sort<int>(Array<int>&);
Una función con el mismo nombre y la misma lista de argumentos que una especialización no es una especialización (consulte sobrecarga de plantillas en function template ).
Argumentos por defecto de funciones no pueden especificarse en especializaciones explícitas de plantillas de función, plantillas de funciones miembro y funciones miembro de plantillas de clase cuando la clase se instancia implícitamente.
Una especialización explícita no puede ser una declaración friend .
|
Esta sección está incompleta
Razón: revisar el requisito de especificación de excepciones en diferentes versiones de C++ |
Miembros de especializaciones
Al definir un miembro de una clase de plantilla explícitamente especializada fuera del cuerpo de la clase, la sintaxis template <> no se utiliza, excepto si es un miembro de una clase de plantilla miembro explícitamente especializada, que se especializa como una clase de plantilla, porque de lo contrario, la sintaxis requeriría que dicha definición comenzara con template < parameters > requerida por la plantilla anidada
template<typename T> struct A { struct B {}; // clase miembro template<class U> // plantilla de clase miembro struct C {}; }; template<> // especialización struct A<int> { void f(int); // función miembro de una especialización }; // template<> no se usa para un miembro de una especialización void A<int>::f(int) { /* ... */ } template<> // especialización de una clase miembro struct A<char>::B { void f(); }; // template<> tampoco se usa para un miembro de una clase miembro especializada void A<char>::B::f() { /* ... */ } template<> // especialización de una plantilla de clase miembro template<class U> struct A<char>::C { void f(); }; // template<> se usa al definir un miembro de una plantilla de clase miembro // explícitamente especializada como plantilla de clase template<> template<class U> void A<char>::C<U>::f() { /* ... */ }
Una especialización explícita de un miembro de datos estático de una plantilla es una definición si la declaración incluye un inicializador; de lo contrario, es una declaración. Estas definiciones deben usar llaves para la inicialización por defecto:
template<> X Q<int>::x; // declaración de un miembro estático template<> X Q<int>::x (); // error: declaración de función template<> X Q<int>::x {}; // definición de un miembro estático inicializado por defecto
Un miembro o un miembro plantilla de una plantilla de clase puede ser especializado explícitamente para una instanciación implícita dada de la plantilla de clase, incluso si el miembro o miembro plantilla está definido en la definición de la plantilla de clase.
template<typename T> struct A { void f(T); // miembro, declarado en la plantilla principal void h(T) {} // miembro, definido en la plantilla principal template<class X1> // plantilla de miembro void g1(T, X1); template<class X2> // plantilla de miembro void g2(T, X2); }; // especialización de un miembro template<> void A<int>::f(int); // especialización de miembro válida incluso si se define en-clase template<> void A<int>::h(int) {} // definición de plantilla de miembro fuera de clase template<class T> template<class X1> void A<T>::g1(T, X1) {} // especialización de plantilla de miembro template<> template<class X1> void A<int>::g1(int, X1); // especialización de plantilla de miembro template<> template<> void A<int>::g2<char>(int, char); // para X2 = char // igual, usando deducción de argumentos de plantilla (X1 = char) template<> template<> void A<int>::g1(int, char);
Un miembro o una plantilla de miembro puede estar anidado dentro de muchas plantillas de clase envolventes. En una especialización explícita para dicho miembro, hay un template <> para cada plantilla de clase envolvente que esté explícitamente especializada.
template<class T1> struct A { template<class T2> struct B { template<class T3> void mf(); }; }; template<> struct A<int>; template<> template<> struct A<char>::B<double>; template<> template<> template<> void A<char>::B<char>::mf<double>();
En dicha declaración anidada, algunos de los niveles pueden permanecer no especializados (excepto que no se puede especializar una plantilla miembro de clase en el ámbito del espacio de nombres si su clase envolvente no está especializada). Para cada uno de esos niveles, la declaración necesita template < arguments > , porque tales especializaciones son en sí mismas plantillas:
template<class T1> class A { template<class T2> class B { template<class T3> // plantilla miembro void mf1(T3); void mf2(); // miembro no plantilla }; }; // especialización template<> // para la A especializada template<class X> // para la B no especializada class A<int>::B { template<class T> void mf1(T); }; // especialización template<> // para la A especializada template<> // para la B especializada template<class T> // para la mf1 no especializada void A<int>::B<double>::mf1(T t) {} // ERROR: B<double> está especializada y es una plantilla miembro, por lo que su A contenedora // también debe estar especializada template<class Y> template<> void A<Y>::B<double>::mf2() {}
Informes de defectos
Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares publicados anteriormente de C++.
| DR | Se aplica a | Comportamiento publicado | Comportamiento correcto |
|---|---|---|---|
| CWG 531 | C++98 |
la sintaxis para definir miembros de especializaciones
explícitas en el ámbito de espacio de nombres no estaba especificada |
especificada |
| CWG 727 | C++98 |
especializaciones parciales y completas no permitidas en
el ámbito de clase |
permitidas en cualquier ámbito |
| CWG 730 | C++98 |
las plantillas de miembros de clases no plantilla
no podían especializarse completamente |
permitido |
| CWG 2478 | C++20 |
no estaba claro si el
constinit
y
consteval
de la
plantilla principal se transfieren a sus especializaciones explícitas |
no se transfieren |
| CWG 2604 | C++11 |
no estaba claro si los atributos de la plantilla
principal se transfieren a sus especializaciones explícitas |
no se transfieren |