Namensräume
Varianten
Aktionen

Partielle 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
Sonstiges
 
 
 
 

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:

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

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)
2) Standardargumente dürfen nicht in der Argumentliste vorkommen.
3) Wenn ein Argument eine Pack-Erweiterung ist, muss es das letzte Argument in der Liste sein.
4) Nicht-Typ-Argumentausdrücke dürfen Template-Parameter verwenden, solange der Parameter mindestens einmal außerhalb eines nicht deduzierbaren Kontexts vorkommt (beachten Sie, dass derzeit nur clang und gcc 12 diese Funktion 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) Nicht-Typ-Template-Argumente dürfen 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

[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.

1) Wenn nur eine Spezialisierung mit den Template-Argumenten übereinstimmt, wird diese Spezialisierung verwendet.
2) Wenn mehr als eine Spezialisierung übereinstimmt, werden partielle Ordnungsregeln verwendet, um zu bestimmen, welche Spezialisierung spezialisierter ist. Die am spezialisiertesten Spezialisierung wird verwendet, wenn sie eindeutig ist (wenn sie nicht eindeutig ist, kann das Programm nicht kompiliert werden).
3) Wenn keine Spezialisierung übereinstimmt, wird das primäre 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", 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

[bearbeiten] Siehe auch