Explicit (full) template specialization
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:
- Funktions-Template
- Klassen-Template
- Variablen-Template (seit C++14)
- Member-Funktion eines Klassen-Templates
- Statisches Datenelement eines Klassen-Templates
- Member-Klasse eines Klassen-Templates
- Member- Enumeration eines Klassen-Templates
- Member-Klassen-Template einer Klasse oder eines Klassen-Templates
- Member-Funktions-Template einer Klasse oder eines Klassen-Templates
- 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.
|
Dieser Abschnitt ist unvollständig
Grund: Überprüfung der Exception-Spezifikationsanforderungen in verschiedenen C++-Versionen |
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 |