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

Ermöglicht die Anpassung des Template-Codes für einen bestimmten Satz von Template-Argumenten.

Inhaltsverzeichnis

Syntax

template <> Deklaration

Jede der folgenden Optionen kann vollständig spezialisiert werden:

  1. Funktions-Template
  2. Klassen-Template
  3. Variablen-Template (seit C++14)
  4. Member-Funktion eines Klassen-Templates
  5. Statisches Datenelement eines Klassen-Templates
  6. Member-Klasse eines Klassen-Templates
  7. Member- Enumeration eines Klassen-Templates
  8. Member-Klassen-Template einer Klasse oder eines Klassen-Templates
  9. Member-Funktions-Template einer Klasse oder eines Klassen-Templates
  10. Member-Variablen-Template einer Klasse oder eines Klassen-Templates (seit C++14)

Zum Beispiel,

#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");
}

Im Detail

Explizite Spezialisierung kann in jedem Gültigkeitsbereich deklariert werden, in dem ihr primäres Template definiert werden kann (was sich vom Gültigkeitsbereich unterscheiden kann, in dem das primäre Template definiert ist; wie bei der Out-of-Class-Spezialisierung eines Member-Templates ). Explizite Spezialisierung muss nach der nicht-spezialisierten Template-Deklaration erscheinen.

namespace N
{
    template<class T> // Primäres Template
    class X { /*...*/ };
    template<>        // Spezialisierung im selben Namensraum
    class X<int> { /*...*/ };
    template<class T> // Primäres Template
    class Y { /*...*/ };
    template<>        // Vorwärtsdeklaration der Spezialisierung für double
    class Y<double>;
}
template<> // OK: Spezialisierung im selben Namensraum
class N::Y<double> { /*...*/ };

Die Spezialisierung muss vor der ersten Verwendung, die eine implizite Instanziierung verursachen würde, in jeder Übersetzungseinheit, in der eine solche Verwendung auftritt, deklariert werden:

class String {};
template<class T>
class Array { /*...*/ };
template<class T> // Primär-Template
void sort(Array<T>& v) { /*...*/ }
void f(Array<String>& v)
{
    sort(v); // instanziiert implizit sort(Array<String>&),
}            // unter Verwendung des Primär-Templates für sort()
template<> // FEHLER: Explizite Spezialisierung von sort(Array<String>)
void sort<String>(Array<String>& v); // nach impliziter Instanziierung

Eine Template-Spezialisierung, die deklariert aber nicht definiert wurde, kann wie jeder andere unvollständiger Typ verwendet werden (z.B. können Zeiger und Referenzen darauf verwendet werden):

template<class T> // Primär-Template
class X;
template<>        // Spezialisierung (deklariert, nicht definiert)
class X<int>;
X<int>* p; // OK: Zeiger auf unvollständigen Typ
X<int> x;  // Fehler: Objekt unvollständigen Typs

Ob eine explizite Spezialisierung einer Funktions- oder Variablen (since C++14) -Template inline / constexpr (since C++11) / constinit / consteval (since C++20) ist, wird durch die explizite Spezialisierung selbst bestimmt, unabhängig davon, ob die primäre Template mit diesem Spezifizierer deklariert ist. Ebenso haben Attribute , die in der Deklaration eines Templates erscheinen, keine Auswirkung auf eine explizite Spezialisierung dieses Templates: (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, nicht inline
template<typename>
[[noreturn]] void h([[maybe_unused]] int i);
template<> void h<int>(int i)
{
    // [[noreturn]] hat keine Wirkung, aber [[maybe_unused]] hat
}

Explizite Spezialisierungen von Funktions-Templates

Beim Spezialisieren eines Funktions-Templates können dessen Template-Argumente weggelassen werden, wenn Template-Argument-Deduktion diese aus den Funktionsargumenten ableiten kann:

template<class T>
class Array { /*...*/ };
template<class T> // Primäres Template
void sort(Array<T>& v);
template<>        // Spezialisierung für T = int
void sort(Array<int>&);
// Nicht nötig zu schreiben
// template<> void sort<int>(Array<int>&);

Eine Funktion mit demselben Namen und derselben Argumentliste wie eine Spezialisierung ist keine Spezialisierung (siehe Template-Überladung in function template ).

Standardfunktionsargumente können nicht in expliziten Spezialisierungen von Funktionsvorlagen, Memberfunktionsvorlagen und Memberfunktionen von Klassenvorlagen angegeben werden, wenn die Klasse implizit instanziiert wird.

Eine explizite Spezialisierung kann keine friend-Deklaration sein.

Mitglieder von Spezialisierungen

Beim Definieren eines Members einer explizit spezialisierten Klassentemplate außerhalb des Klassenkörpers wird die Syntax template <> nicht verwendet, es sei denn, es handelt sich um ein Member eines explizit spezialisierten Member-Klassentemplates, das als Klassentemplate spezialisiert ist, da andernfalls die Syntax erfordern würde, dass eine solche Definition mit template < parameters > beginnen müsste, wie es durch das geschachtelte Template erforderlich ist

template<typename T>
struct A
{
    struct B {};      // Mitgliedsklasse
    template<class U> // Mitgliedsklassen-Template
    struct C {};
};
template<> // Spezialisierung
struct A<int> 
{
    void f(int); // Mitgliedsfunktion einer Spezialisierung
};
// template<> wird nicht für ein Mitglied einer Spezialisierung verwendet
void A<int>::f(int) { /* ... */ }
template<> // Spezialisierung einer Mitgliedsklasse
struct A<char>::B
{
    void f();
};
// template<> wird auch nicht für ein Mitglied einer spezialisierten Mitgliedsklasse verwendet
void A<char>::B::f() { /* ... */ }
template<> // Spezialisierung eines Mitgliedsklassen-Templates
template<class U>
struct A<char>::C
{
    void f();
};
// template<> wird verwendet, wenn ein Mitglied eines explizit
// spezialisierten Mitgliedsklassen-Templates als Klassentemplate definiert wird
template<>
template<class U>
void A<char>::C<U>::f() { /* ... */ }


Eine explizite Spezialisierung eines statischen Datenelements einer Vorlage ist eine Definition, wenn die Deklaration einen Initialisierer enthält; andernfalls ist es eine Deklaration. Diese Definitionen müssen geschweifte Klammern für die Standardinitialisierung verwenden:

template<>
X Q<int>::x;    // Deklaration eines statischen Members
template<>
X Q<int>::x (); // Fehler: Funktionsdeklaration
template<>
X Q<int>::x {}; // Definition eines standardinitialisierten statischen Members

Ein Member oder ein Member-Template einer Klassenvorlage kann explizit für eine gegebene implizite Instanziierung der Klassenvorlage spezialisiert werden, selbst wenn der Member oder das Member-Template in der Klassenvorlagendefinition definiert ist.

template<typename T>
struct A
{
    void f(T);         // Mitglied, deklariert im primären Template
    void h(T) {}       // Mitglied, definiert im primären Template
    template<class X1> // Mitgliedstemplate
    void g1(T, X1);
    template<class X2> // Mitgliedstemplate
    void g2(T, X2);
};
// Spezialisierung eines Mitglieds
template<>
void A<int>::f(int);
// Mitgliederspezialisierung OK, auch wenn in der Klasse definiert
template<>
void A<int>::h(int) {}
// Definition des Mitgliedstemplates außerhalb der Klasse
template<class T>
template<class X1>
void A<T>::g1(T, X1) {}
// Mitgliedstemplatespezialisierung
template<>
template<class X1>
void A<int>::g1(int, X1);
// Mitgliedstemplatespezialisierung
template<>
template<>
void A<int>::g2<char>(int, char); // für X2 = char
// dasselbe, mit Template-Argumentableitung (X1 = char)
template<> 
template<>
void A<int>::g1(int, char);

Ein Member oder ein Member-Template kann innerhalb vieler umschließender Klassentemplates verschachtelt sein. In einer expliziten Spezialisierung für einen solchen Member gibt es ein template <> für jedes umschließende Klassentemplate, das explizit spezialisiert wird.

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>();

In einer solchen geschachtelten Deklaration können einige Ebenen unspezialisiert bleiben (außer dass ein Klassentemplate-Member nicht im Namensbereich spezialisiert werden kann, wenn seine umschließende Klasse unspezialisiert ist). Für jede dieser Ebenen benötigt die Deklaration template < arguments > , da solche Spezialisierungen selbst Templates sind:

template<class T1>
class A
{
    template<class T2>
    class B
    {
        template<class T3> // Mitgliedstemplate
        void mf1(T3);
        void mf2();        // Nicht-Template-Mitglied
    };
};
// Spezialisierung
template<>        // für das spezialisierte A
template<class X> // für das unspezialisierte B
class A<int>::B
{
    template<class T>
    void mf1(T);
};
// Spezialisierung
template<>        // für das spezialisierte A
template<>        // für das spezialisierte B
template<class T> // für das unspezialisierte mf1
void A<int>::B<double>::mf1(T t) {}
// FEHLER: B<double> ist spezialisiert und ein Mitgliedstemplate, daher muss
// sein umschließendes A ebenfalls spezialisiert sein
template<class Y>
template<>
void A<Y>::B<double>::mf2() {}

Fehlerberichte

Die folgenden verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.

DR Angewendet auf Verhalten wie veröffentlicht Korrektes Verhalten
CWG 531 C++98 die Syntax zur Definition von Mitgliedern expliziter
Spezialisierungen im Namensbereich war nicht spezifiziert
spezifiziert
CWG 727 C++98 partielle und vollständige Spezialisierungen nicht erlaubt in
Klassenbereich
in jedem Bereich erlaubt
CWG 730 C++98 Mitgliedstemplates von Nicht-Template-
Klassen konnten nicht vollständig spezialisiert werden
erlaubt
CWG 2478 C++20 es war unklar, ob die constinit und consteval des
Primärtemplates in seine expliziten Spezialisierungen übernommen werden
nicht übernommen
CWG 2604 C++11 es war unklar, ob die Attribute des Primär-
templates in seine expliziten Spezialisierungen übernommen werden
nicht übernommen

Siehe auch