Partielle Template-Spezialisierung
Ermöglicht die Anpassung von Klassen- und Variablentemplates(seit C++14) für eine gegebene Kategorie von Template-Argumenten.
Inhalt |
[bearbeiten] 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 eines zuvor deklarierten Klassentemplates identifiziert und declarator den Namen eines zuvor deklarierten Variablentemplates identifiziert(seit C++14).
Partielle Spezialisierungen können in jedem Gültigkeitsbereich deklariert werden, in dem ihre primäre Template-Definition zulässig ist (was sich vom Gültigkeitsbereich der primären Template-Definition unterscheiden kann; z. B. bei Out-of-Class-Spezialisierung eines Member-Templates). Partielle Spezialisierungen müssen nach der nicht spezialisierten Template-Deklaration erscheinen.
Zum Beispiel,
template<class T1, class T2, int I> class A {}; // primary template template<class T, int I> class A<T, T*, I> {}; // #1: partial specialization where T2 is a pointer to T1 template<class T, class T2, int I> class A<T*, T2, I> {}; // #2: partial specialization where T1 is a pointer template<class T> class A<int, T*, 5> {}; // #3: partial specialization where // T1 is int, I is 5, and T2 is a pointer template<class X, class T, int I> class A<X, T*, I> {}; // #4: partial specialization where T2 is a pointer
Beispiele für partielle Spezialisierungen in der Standardbibliothek sind std::unique_ptr, das eine partielle Spezialisierung für Array-Typen hat.
[bearbeiten] Die Argumentliste
Für die argument-list einer partiellen Template-Spezialisierung gelten folgende Einschränkungen:
template<class T1, class T2, int I> class B {}; // primary template template<class X, class Y, int N> class B<X, Y, N> {}; // error
|
Darüber hinaus muss die Spezialisierung spezialisierter sein als das primäre 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
[bearbeiten] Namensauflösung
Partielle Template-Spezialisierungen werden nicht über die Namensauflösung gefunden. Erst wenn das primäre Template durch Namensauflösung gefunden wird, werden seine partiellen Spezialisierungen berücksichtigt. Insbesondere eine `using`-Deklaration, die ein primäres Template sichtbar macht, macht auch partielle Spezialisierungen sichtbar.
namespace N { template<class T1, class T2> class Z {}; // primary template } using N::Z; // refers to the primary template namespace N { template<class T> class Z<T, T*> {}; // partial specialization } Z<int, int*> z; // name lookup finds N::Z (the primary template), the // partial specialization with T = int is then used
[bearbeiten] Partielle Ordnung
Wenn ein Klassen- oder Variablentemplate(seit C++14) 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", dass "A eine Teilmenge der Typen akzeptiert, die B akzeptiert".
Formal wird zur Festlegung der "spezialisierter als"-Beziehung zwischen partiellen Spezialisierungen jede zunächst in ein fiktives Funktions-Template umgewandelt, wie folgt:
- das erste Funktions-Template hat die gleichen Template-Parameter wie die erste partielle Spezialisierung und hat nur einen Funktionsparameter, dessen Typ eine Klassentemplate-Spezialisierung mit allen Template-Argumenten aus der ersten partiellen Spezialisierung ist.
- das zweite Funktions-Template hat die gleichen Template-Parameter wie die zweite partielle Spezialisierung und hat nur einen Funktionsparameter, dessen Typ eine Klassentemplate-Spezialisierung mit allen Template-Argumenten aus der zweiten partiellen Spezialisierung ist.
Die Funktions-Templates werden dann wie bei Funktions-Template-Überladung eingestuft.
template<int I, int J, class T> struct X {}; // primary template template<int I, int J> struct X<I, J, int> { static const int s = 1; }; // partial specialization #1 // fictitious function template for #1 is // 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; }; // partial specialization #2 // fictitious function template for #2 is // template<int I> void f(X<I, I, int>); #B int main() { X<2, 2, int> x; // both #1 and #2 match // partial ordering for function templates: // #A from #B: void(X<I, J, int>) from void(X<U1, U1, int>): deduction OK // #B from #A: void(X<I, I, int>) from void(X<U1, U2, int>): deduction fails // #B is more specialized // #2 is the specialization that is instantiated std::cout << x.s << '\n'; // prints 2 }
[bearbeiten] Member von partiellen Spezialisierungen
Die Template-Parameterliste und die Template-Argumentliste eines Members einer partiellen Spezialisierung müssen mit der Parameterliste und der Argumentliste der partiellen Spezialisierung übereinstimmen.
Ähnlich wie bei Membern von primären Templates müssen sie nur dann definiert werden, wenn sie im Programm verwendet werden.
Member von partiellen Spezialisierungen haben keine Beziehung zu den Membern des primären Templates.
Explizite (vollständige) Spezialisierung eines Members einer partiellen Spezialisierung wird auf die gleiche Weise deklariert wie eine explizite Spezialisierung des primären Templates.
template<class T, int I> // primary template struct A { void f(); // member declaration }; template<class T, int I> void A<T, I>::f() {} // primary template member definition // partial specialization template<class T> struct A<T, 2> { void f(); void g(); void h(); }; // member of partial specialization template<class T> void A<T, 2>::g() {} // explicit (full) specialization // of a member of partial specialization template<> void A<char, 2>::h() {} int main() { A<char, 0> a0; A<char, 2> a2; a0.f(); // OK, uses primary template’s member definition a2.g(); // OK, uses partial specialization's member definition a2.h(); // OK, uses fully-specialized definition of // the member of a partial specialization a2.f(); // error: no definition of f() in the partial // specialization A<T,2> (the primary template is not used) }
Wenn ein primäres Template ein Member eines anderen Klassentemplates ist, sind seine partiellen Spezialisierungen Member des umschließenden Klassentemplates. Wenn das umschließende Template instanziiert wird, wird die Deklaration jeder Member- partiellen Spezialisierung ebenfalls instanziiert (auf die gleiche Weise wie Deklarationen, aber nicht Definitionen, aller anderen Member eines Templates instanziiert werden).
Wenn das primäre Member-Template für eine gegebene (implizite) Spezialisierung des umschließenden Klassentemplates explizit (vollständig) spezialisiert ist, werden die partiellen Spezialisierungen des Member-Templates für diese Spezialisierung des umschließenden Klassentemplates ignoriert.
Wenn eine partielle Spezialisierung des Member-Templates für eine gegebene (implizite) Spezialisierung des umschließenden Klassentemplates explizit spezialisiert ist, werden das primäre Member-Template und seine anderen partiellen Spezialisierungen für diese Spezialisierung des umschließenden Klassentemplates weiterhin berücksichtigt.
template<class T> struct A // enclosing class template { template<class T2> struct B {}; // primary member template template<class T2> struct B<T2*> {}; // partial specialization of member template }; template<> template<class T2> struct A<short>::B {}; // full specialization of primary member template // (will ignore the partial) A<char>::B<int*> abcip; // uses partial specialization T2=int A<short>::B<int*> absip; // uses full specialization of the primary (ignores partial) A<char>::B<int> abci; // uses primary
[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 727 | C++98 | partielle und vollständige Spezialisierungen nicht erlaubt in Klassengültigkeitsbereich |
erlaubt in jedem Gültigkeitsbereich |
| CWG 1315 | C++98 | Template-Parameter konnte nicht in Nicht-Typ verwendet werden Template-Argumente außer ID-Ausdrücken |
Ausdrücke ok, solange deduzierbar |
| CWG 1495 | C++11 | die Spezifikation war unklar, wenn Parameter-Pack beteiligt war | die Spezialisierung muss spezialisierter sein |
| CWG 1711 | C++14 | fehlende Spezifikation von Variablentemplate-Partialspezialisierungen | Unterstützung für Variablentemplates hinzufügen |
| CWG 1819 | C++98 | akzeptable Gültigkeitsbereiche für die Definition von partiellen Spezialisierungen | partielle Spezialisierung kann deklariert werden im selben Gültigkeitsbereich wie primäre Templates |
| CWG 2330 | C++14 | fehlende Verweise auf Variablentemplates | Unterstützung für Variablentemplates hinzufügen |