Namespaces
Variants

Partial 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
Miscellaneous

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:

1) Die Argumentliste darf nicht identisch mit der nicht-spezialisierten Argumentliste sein (sie muss etwas spezialisieren):
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)
2) Standardargumente dürfen nicht in der Argumentliste erscheinen
3) Wenn ein Argument eine Pack-Erweiterung ist, muss es das letzte Argument in der Liste sein
4) Konstante Argumentausdrücke können Template-Parameter verwenden, solange der Parameter mindestens einmal außerhalb eines Nicht-Deduzierungs-Kontexts erscheint (beachten Sie, dass nur Clang und GCC 12 diese Funktion derzeit unterstützen):
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
5) Ein konstanter Template-Argument kann keinen Template-Parameter spezialisieren, dessen Typ von einem Parameter der Spezialisierung abhängt:
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.

1) Wenn nur eine Spezialisierung auf die Template-Argumente passt, wird diese Spezialisierung verwendet
2) Wenn mehr als eine Spezialisierung übereinstimmt, werden partielle Ordnungsregeln verwendet, um zu bestimmen, welche Spezialisierung spezialisierter ist. Die am stärksten spezialisierte Spezialisierung wird verwendet, falls sie eindeutig ist (falls sie nicht eindeutig ist, kann das Programm nicht kompiliert werden)
3) Wenn keine Spezialisierungen übereinstimmen, wird das Primär-Template verwendet
// 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

Siehe auch