Partial template specialization
Ermöglicht die Anpassung von Klassen- und Variablen (seit C++14) Templates für eine bestimmte Kategorie von Template-Argumenten.
Inhaltsverzeichnis |
Syntax
template
<
parameter-list
>
class-key
class-head-name
<
argument-list
>
declaration
|
(1) | ||||||||
template
<
parameter-list
>
decl-specifier-seq
declarator
<
argument-list
>
initializer
(optional)
|
(2) | (seit C++14) | |||||||
wobei class-head-name den Namen einer zuvor deklarierten class template identifiziert und declarator den Namen einer zuvor deklarierten variable template identifiziert (since C++14) .
Partielle 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 ). Partielle Spezialisierung muss nach der nicht-spezialisierten Template-Deklaration erscheinen.
Zum Beispiel,
template<class T1, class T2, int I> class A {}; // Primäres Template template<class T, int I> class A<T, T*, I> {}; // #1: Partielle Spezialisierung, bei der T2 ein Zeiger auf T1 ist template<class T, class T2, int I> class A<T*, T2, I> {}; // #2: Partielle Spezialisierung, bei der T1 ein Zeiger ist template<class T> class A<int, T*, 5> {}; // #3: Partielle Spezialisierung, bei der // T1 int ist, I 5 ist und T2 ein Zeiger ist template<class X, class T, int I> class A<X, T*, I> {}; // #4: Partielle Spezialisierung, bei der T2 ein Zeiger ist
Beispiele für partielle Spezialisierungen in der Standardbibliothek umfassen std::unique_ptr , welches eine partielle Spezialisierung für Array-Typen besitzt.
Die Argumentenliste
Die folgenden Einschränkungen gelten für die Argumentenliste einer partiellen Template-Spezialisierung:
template<class T1, class T2, int I> class B {}; // primary template template<class X, class Y, int N> class B<X, Y, N> {}; // error
|
Außerdem muss die Spezialisierung spezialisierter sein als das Primär-Template template<int N, typename T1, typename... Ts> struct B; template<typename... Ts> struct B<0, Ts...> {}; // Error: not more specialized |
(seit C++11) |
template<int I, int J> struct A {}; template<int I> struct A<I + 5, I * 2> {}; // error, I is not deducible template<int I, int J, int K> struct B {}; template<int I> struct B<I, I * 2, 2> {}; // OK: first parameter is deducible
template<class T, T t> struct C {}; // primary template template<class T> struct C<T, 1>; // error: type of the argument 1 is T, // which depends on the parameter T template<int X, int (*array_ptr)[X]> class B {}; // primary template int array[5]; template<int X> class B<X, &array> {}; // error: type of the argument &array is // int(*)[X], which depends on the parameter X
Namenssuche
Partielle Template-Spezialisierungen werden nicht durch Namenssuche gefunden. Nur wenn das primäre Template durch Namenssuche gefunden wird, werden seine partiellen Spezialisierungen berücksichtigt. Insbesondere macht eine using-Deklaration, die ein primäres Template sichtbar macht, auch partielle Spezialisierungen sichtbar:
namespace N { template<class T1, class T2> class Z {}; // Primäres Template } using N::Z; // bezieht sich auf das primäre Template namespace N { template<class T> class Z<T, T*> {}; // Partielle Spezialisierung } Z<int, int*> z; // Namenssuche findet N::Z (das primäre Template), die // partielle Spezialisierung mit T = int wird dann verwendet
Partielle Ordnung
Wenn eine Klassen- oder Variablen- (seit C++14) Template instanziiert wird und partielle Spezialisierungen verfügbar sind, muss der Compiler entscheiden, ob das primäre Template oder eine seiner partiellen Spezialisierungen verwendet wird.
// given the template A as defined above A<int, int, 1> a1; // no specializations match, uses primary template A<int, int*, 1> a2; // uses partial specialization #1 (T = int, I = 1) A<int, char*, 5> a3; // uses partial specialization #3, (T = char) A<int, char*, 1> a4; // uses partial specialization #4, (X = int, T = char, I = 1) A<int*, int*, 2> a5; // error: matches #2 (T = int, T2 = int*, I= 2) // matches #4 (X = int*, T = int, I = 2) // neither one is more specialized than the other
Informell bedeutet "A ist spezialisierter als B": "A akzeptiert eine Teilmenge der Typen, die B akzeptiert".
Formal wird zur Feststellung der Beziehung "spezialisierter als" zwischen partiellen Spezialisierungen jede zunächst in eine fiktive Funktionsvorlage wie folgt umgewandelt:
- die erste Funktionsvorlage hat dieselben Vorlagenparameter wie die erste partielle Spezialisierung und hat nur einen Funktionsparameter, dessen Typ eine Klassenvorlagenspezialisierung mit allen Vorlagenargumenten aus der ersten partiellen Spezialisierung ist
- die zweite Funktionsvorlage hat dieselben Vorlagenparameter wie die zweite partielle Spezialisierung und hat nur einen Funktionsparameter, dessen Typ eine Klassenvorlagenspezialisierung mit allen Vorlagenargumenten aus der zweiten partiellen Spezialisierung ist
Die Funktionsvorlagen werden dann so gerankt, als ob für function template overloading .
template<int I, int J, class T> struct X {}; // Primäres Template template<int I, int J> struct X<I, J, int> { static const int s = 1; }; // Partielle Spezialisierung #1 // Fiktive Funktionsvorlage für #1 ist // template<int I, int J> void f(X<I, J, int>); #A template<int I> struct X<I, I, int> { static const int s = 2; }; // Partielle Spezialisierung #2 // Fiktive Funktionsvorlage für #2 ist // template<int I> void f(X<I, I, int>); #B int main() { X<2, 2, int> x; // Sowohl #1 als auch #2 passen // Partielle Ordnung für Funktionsvorlagen: // #A von #B: void(X<I, J, int>) von void(X<U1, U1, int>): Deduktion OK // #B von #A: void(X<I, I, int>) von void(X<U1, U2, int>): Deduktion fehlgeschlagen // #B ist spezialisierter // #2 ist die Spezialisierung, die instanziiert wird std::cout << x.s << '\n'; // gibt 2 aus }
Mitglieder von partiellen Spezialisierungen
Die Vorlagenparameterliste und die Vorlagenargumentliste eines Members einer partiellen Spezialisierung müssen mit der Parameterliste und der Argumentliste der partiellen Spezialisierung übereinstimmen.
Genau wie bei Mitgliedern von Primärtemplates müssen sie nur definiert werden, wenn sie im Programm verwendet werden.
Mitglieder von partiellen Spezialisierungen stehen in keiner Beziehung zu den Mitgliedern der primären Vorlage.
Explizite (vollständige) Spezialisierung eines Members einer partiellen Spezialisierung wird auf die gleiche Weise dekllariert wie eine explizite Spezialisierung des primären Templates.
template<class T, int I> // Primäres Template struct A { void f(); // Member-Deklaration }; template<class T, int I> void A<T, I>::f() {} // Member-Definition des primären Templates // Partielle Spezialisierung template<class T> struct A<T, 2> { void f(); void g(); void h(); }; // Member der partiellen Spezialisierung template<class T> void A<T, 2>::g() {} // Explizite (vollständige) Spezialisierung // eines Members der partiellen Spezialisierung template<> void A<char, 2>::h() {} int main() { A<char, 0> a0; A<char, 2> a2; a0.f(); // OK, verwendet Member-Definition des primären Templates a2.g(); // OK, verwendet Member-Definition der partiellen Spezialisierung a2.h(); // OK, verwendet vollständig spezialisierte Definition // des Members einer partiellen Spezialisierung a2.f(); // Fehler: Keine Definition von f() in der partiellen // Spezialisierung A<T,2> (das primäre Template wird nicht verwendet) }
Wenn eine primäre Vorlage ein Mitglied einer anderen Klassenvorlage ist, sind ihre partiellen Spezialisierungen Mitglieder der umschließenden Klassenvorlage. Wenn die umschließende Vorlage instanziiert wird, wird die Deklaration jeder partiellen Mitgliedsspezialisierung ebenfalls instanziiert (auf die gleiche Weise, wie Deklarationen, aber nicht Definitionen aller anderen Mitglieder einer Vorlage instanziiert werden).
Wenn das primäre Member-Template für eine gegebene (implizite) Spezialisierung der umschließenden Klassentemplate explizit (vollständig) spezialisiert ist, werden die partiellen Spezialisierungen des Member-Templates für diese Spezialisierung der umschließenden Klassentemplate ignoriert.
Wenn eine partielle Spezialisierung des Member-Templates für eine gegebene (implizite) Spezialisierung des umschließenden Klassentemplates explizit spezialisiert wird, werden das primäre Member-Template und seine anderen partiellen Spezialisierungen weiterhin für diese Spezialisierung des umschließenden Klassentemplates in Betracht gezogen.
template<class T> struct A // umschließende Klassenvorlage { template<class T2> struct B {}; // primäre Mitgliedsvorlage template<class T2> struct B<T2*> {}; // partielle Spezialisierung der Mitgliedsvorlage }; template<> template<class T2> struct A<short>::B {}; // vollständige Spezialisierung der primären Mitgliedsvorlage // (ignoriert die partielle Spezialisierung) A<char>::B<int*> abcip; // verwendet partielle Spezialisierung T2=int A<short>::B<int*> absip; // verwendet vollständige Spezialisierung der primären Vorlage (ignoriert partielle) A<char>::B<int> abci; // verwendet primäre Vorlage
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 727 | C++98 |
Partielle und vollständige Spezialisierungen nicht erlaubt in
Klassenbereich |
Erlaubt in jedem Bereich |
| CWG 1315 | C++98 |
Template-Parameter konnte nicht in konstanten
Template-Argumenten außer ID-Ausdrücken verwendet werden |
Ausdrücke OK solange ableitbar |
| CWG 1495 | C++11 | Die Spezifikation war unklar bei Parameter-Paketen | Die Spezialisierung muss spezieller sein |
| CWG 1711 | C++14 | Fehlende Spezifikation von Variablen-Template-Partialspezialisierungen | Unterstützung für Variablen-Templates hinzufügen |
| CWG 1819 | C++98 | Zulässige Bereiche für Definition von Partialspezialisierung |
Partialspezialisierung kann deklariert werden
im selben Bereich wie primäre Templates |
| CWG 2330 | C++14 | Fehlende Referenzen zu Variablen-Templates | Unterstützung für Variablen-Templates hinzufügen |