Namensräume
Varianten
Aktionen

Explizite (vollständige) Template-Spezialisierung

Von cppreference.com
< cpp‎ | Sprache
 
 
C++ Sprache
Allgemeine Themen
Kontrollfluss
Bedingte Ausführungsaussagen
if
Iterationsanweisungen (Schleifen)
for
Bereichs-for (C++11)
Sprunganweisungen
Funktionen
Funktionsdeklaration
Lambda-Funktionsausdruck
inline-Spezifizierer
Dynamische Ausnahmespezifikationen (bis C++17*)
noexcept-Spezifizierer (C++11)
Ausnahmen
Namensräume
Typen
Spezifizierer
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Speicherdauer-Spezifizierer
Initialisierung
Ausdrücke
Alternative Darstellungen
Literale
Boolesch - Ganzzahl - Gleitkommazahl
Zeichen - String - nullptr (C++11)
Benutzerdefinierte (C++11)
Dienstprogramme
Attribute (C++11)
Typen
typedef-Deklaration
Typalias-Deklaration (C++11)
Umwandlungen
Speicherzuweisung
Klassen
Klassenspezifische Funktionseigenschaften
explicit (C++11)
static

Spezielle Member-Funktionen
Templates
Schablonenspezialisierung
Parameter-Pakete (C++11)
Sonstiges
 
 
 
 

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

Inhalt

[bearbeiten] Syntax

template <> Deklaration

Jedes der Folgenden kann vollständig spezialisiert werden

  1. Funktions-Template
  2. Klassentemplate
  3. Variable Template(seit C++14)
  4. Member-Funktion eines Klassen-Templates
  5. Statisches Daten-Member eines Klassen-Templates
  6. Member-Klasse eines Klassen-Templates
  7. Member-Aufzählung 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-Variable 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");
}

[bearbeiten] Im Detail

Explizite Spezialisierungen können 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; z. B. bei Out-of-Class-Spezialisierung eines Member-Templates). Explizite Spezialisierungen müssen nach der nicht spezialisierten Template-Deklaration erscheinen.

namespace N
{
    template<class T> // primary template
    class X { /*...*/ };
    template<>        // specialization in same namespace
    class X<int> { /*...*/ };
 
    template<class T> // primary template
    class Y { /*...*/ };
    template<>        // forward declare specialization for double
    class Y<double>;
}
 
template<> // OK: specialization in same namespace
class N::Y<double> { /*...*/ };

Spezialisierungen müssen vor der ersten Verwendung, die eine implizite Instanziierung auslösen würde, in jeder Translation Unit deklariert werden, in der eine solche Verwendung auftritt.

class String {};
 
template<class T>
class Array { /*...*/ };
 
template<class T> // primary template
void sort(Array<T>& v) { /*...*/ }
 
void f(Array<String>& v)
{
    sort(v); // implicitly instantiates sort(Array<String>&), 
}            // using the primary template for sort()
 
template<> // ERROR: explicit specialization of sort(Array<String>)
void sort<String>(Array<String>& v); // after implicit instantiation

Eine deklarierte, aber nicht definierte Template-Spezialisierung kann wie jeder andere unvollständige Typ verwendet werden (z. B. können Zeiger und Referenzen darauf verwendet werden).

template<class T> // primary template
class X;
template<>        // specialization (declared, not defined)
class X<int>;
 
X<int>* p; // OK: pointer to incomplete type
X<int> x;  // error: object of incomplete type

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

[bearbeiten] Explizite Spezialisierungen von Funktions-Templates

Beim Spezialisieren eines Funktions-Templates können die Template-Argumente weggelassen werden, wenn Template-Argument-Deduction diese aus den Funktionsargumenten ableiten kann.

template<class T>
class Array { /*...*/ };
 
template<class T> // primary template
void sort(Array<T>& v);
template<>        // specialization for T = int
void sort(Array<int>&);
 
// no need to write
// template<> void sort<int>(Array<int>&);

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

Standard-Funktionsargumente können in expliziten Spezialisierungen von Funktions-Templates, Member-Funktions-Templates und Member-Funktionen von Klassen-Templates nicht angegeben werden, wenn die Klasse implizit instanziiert wird.

Eine explizite Spezialisierung kann keine friend-Deklaration sein.

[bearbeiten] Member von Spezialisierungen

Bei der Definition eines Members eines explizit spezialisierten Klassen-Templates außerhalb des Klassenkörpers wird die Syntax template<> nicht verwendet, es sei denn, es handelt sich um einen Member einer explizit spezialisierten Member-Klassen-Template, die als Klassen-Template spezialisiert wird, da andernfalls die Syntax eine solche Definition von vornherein erfordern würde template<parameter;>, die vom geschachtelten Template benötigt wird.

template<typename T>
struct A
{
    struct B {};      // member class 
 
    template<class U> // member class template
    struct C {};
};
 
template<> // specialization
struct A<int> 
{
    void f(int); // member function of a specialization
};
// template<> not used for a member of a specialization
void A<int>::f(int) { /* ... */ }
 
template<> // specialization of a member class
struct A<char>::B
{
    void f();
};
// template<> not used for a member of a specialized member class either
void A<char>::B::f() { /* ... */ }
 
template<> // specialization of a member class template
template<class U>
struct A<char>::C
{
    void f();
};
 
// template<> is used when defining a member of an explicitly
// specialized member class template specialized as a class template
template<>
template<class U>
void A<char>::C<U>::f() { /* ... */ }


Eine explizite Spezialisierung eines statischen Daten-Members eines Templates ist eine Definition, wenn die Deklaration einen Initialisierer enthält; andernfalls ist sie eine Deklaration. Diese Definitionen müssen geschweifte Klammern für die Standardinitialisierung verwenden.

template<>
X Q<int>::x;    // declaration of a static member
template<>
X Q<int>::x (); // error: function declaration
template<>
X Q<int>::x {}; // definition of a default-initialized static member

Ein Member oder ein Member-Template einer Klasse kann für eine gegebene implizite Instanziierung der Klasse explizit spezialisiert werden, auch wenn der Member oder das Member-Template in der Definition des Klassen-Templates definiert ist.

template<typename T>
struct A
{
    void f(T);         // member, declared in the primary template
 
    void h(T) {}       // member, defined in the primary template
 
    template<class X1> // member template
    void g1(T, X1);
 
    template<class X2> // member template
    void g2(T, X2);
};
 
// specialization of a member
template<>
void A<int>::f(int);
 
// member specialization OK even if defined in-class
template<>
void A<int>::h(int) {}
 
// out of class member template definition
template<class T>
template<class X1>
void A<T>::g1(T, X1) {}
 
// member template specialization
template<>
template<class X1>
void A<int>::g1(int, X1);
 
// member template specialization
template<>
template<>
void A<int>::g2<char>(int, char); // for X2 = char
 
// same, using template argument deduction (X1 = char)
template<> 
template<>
void A<int>::g1(int, char);

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

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 un spezialisiert bleiben (außer dass ein Klassen-Member-Template im Namespace-Scope nicht spezialisiert werden kann, wenn seine umschließende Klasse un spezialisiert ist). Für jede dieser Ebenen benötigt die Deklaration template<argumente;>, da solche Spezialisierungen selbst Templates sind.

template<class T1>
class A
{
    template<class T2>
    class B
    {
        template<class T3> // member template
        void mf1(T3);
 
        void mf2();        // non-template member
    };
};
 
// specialization
template<>        // for the specialized A
template<class X> // for the unspecialized B
class A<int>::B
{
    template<class T>
    void mf1(T);
};
 
// specialization
template<>        // for the specialized A
template<>        // for the specialized B
template<class T> // for the unspecialized mf1
void A<int>::B<double>::mf1(T t) {}
 
// ERROR: B<double> is specialized and is a member template, so its enclosing A
// must be specialized also
template<class Y>
template<>
void A<Y>::B<double>::mf2() {}

[bearbeiten] Fehlerberichte

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

DR angewendet auf Verhalten wie veröffentlicht Korrigiertes Verhalten
CWG 531 C++98 die Syntax für die Definition von Membern von expliziten
Spezialisierungen im Namespace-Scope war nicht spezifiziert
spezifiziert
CWG 727 C++98 partielle und vollständige Spezialisierungen nicht in
Klassenscope erlaubt
in jedem Scope erlaubt
CWG 730 C++98 Member-Templates 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ären Templates auf seine expliziten Spezialisierungen übertragen werden
nicht übertragen
CWG 2604 C++11 es war unklar, ob die Attribute des primären
Templates auf seine expliziten Spezialisierungen übertragen werden
nicht übertragen

[bearbeiten] Siehe auch