Namespaces
Variants

Explicit (full) template specialization

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
Template specialization
Parameter packs (C++11)
Miscellaneous

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:

  1. plantilla de función
  2. plantilla de clase
  3. plantilla de variable (desde C++14)
  4. función miembro de una plantilla de clase
  5. miembro de datos estático de una plantilla de clase
  6. clase miembro de una plantilla de clase
  7. enumeración miembro de una plantilla de clase
  8. plantilla de clase miembro de una clase o plantilla de clase
  9. plantilla de función miembro de una clase o plantilla de clase
  10. 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 .

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

Véase también