Template arguments
Damit eine Vorlage instanziiert werden kann, muss jeder Template-Parameter durch ein entsprechendes Template-Argument ersetzt werden. Die Argumente werden entweder explizit angegeben, abgeleitet oder standardmäßig verwendet.
Jeder Parameter in der template-parameter-list (siehe template identifier syntax ) gehört zu einer der folgenden Kategorien:
- konstanter Template-Argument
- Typ-Template-Argument
- Template-Template-Argument
Inhaltsverzeichnis |
Konstante Template-Argumente
Auch bekannt als non-type template arguments (siehe unten ).
|
Das Template-Argument, das mit einem konstanten Template-Parameter verwendet werden kann, kann jeder manifestly constant-evaluated expression sein. |
(until C++11) |
|
Das Template-Argument, das mit einem konstanten Template-Parameter verwendet werden kann, kann jeder initializer clause sein. Wenn die Initialisierer-Klausel ein Ausdruck ist, muss dieser manifestly constant-evaluated sein. |
(since C++11) |
Gegeben sei der
Typ
der
konstanten Template-Parameterdeklaration
als
T
und das für den Parameter bereitgestellte Template-Argument als
E
.
|
Die erfundene Deklaration T x = E ; muss die semantischen Einschränkungen für die Definition einer constexpr Variable mit statischer Speicherdauer erfüllen. |
(seit C++20) |
|
Falls
Falls ein abgeleiteter Parametertyp kein struktureller Typ ist, ist das Programm fehlerhaft. Für konstante Template-Parameterpakete, deren Typ einen Platzhaltertyp verwendet, wird der Typ für jedes Template-Argument unabhängig abgeleitet und muss nicht übereinstimmen. |
(seit C++17) |
template<auto n> struct B { /* ... */ }; B<5> b1; // OK: Der Typ des konstanten Template-Parameters ist int B<'a'> b2; // OK: Der Typ des konstanten Template-Parameters ist char B<2.5> b3; // Fehler (bis C++20): Der Typ des konstanten Template-Parameters kann nicht double sein // C++20 abgeleitener Klassen-Typ-Platzhalter, Klassen-Template-Argumente werden an der // Aufrufstelle abgeleitet template<std::array arr> void f(); f<std::array<double, 8>{}>(); template<auto...> struct C {}; C<'C', 0, 2L, nullptr> x; // OK
Der Wert eines konstanten Template-Parameters
P
vom
(möglicherweise deduzierten)
(since C++17)
Typ
T
wird aus seinem Template-Argument
A
wie folgt bestimmt:
|
(bis C++11) |
|
(seit C++11)
(bis C++20) |
|
(seit C++20) |
template<int i> struct C { /* ... */ }; C<{42}> c1; // OK template<auto n> struct B { /* ... */ }; struct J1 { J1* self = this; }; B<J1{}> j1; // Fehler: Initialisierung des Template-Parameter-Objekts // ist kein konstanter Ausdruck struct J2 { J2 *self = this; constexpr J2() {} constexpr J2(const J2&) {} }; B<J2{}> j2; // Fehler: Das Template-Parameter-Objekt ist nicht // template-argument-equivalent zum eingeführten Temporärwert
|
Die folgenden Einschränkungen gelten bei der Instanziierung von Templates mit konstanten Template-Parametern:
Dies impliziert insbesondere, dass String-Literale, Adressen von Array-Elementen und Adressen nicht-statischer Member nicht als Template-Argumente zur Instanziierung von Templates verwendet werden können, deren entsprechende konstante Template-Parameter Zeiger auf Objekte sind. |
(bis C++17) |
|
Konstante Template-Parameter vom Referenz- oder Zeigertyp und nicht-statische Datenelemente vom Referenz- oder Zeigertyp in einem konstanten Template-Parameter vom Klassentyp und seinen Unterobjekten (seit C++20) dürfen nicht verweisen auf/die Adresse sein von
|
(seit C++17) |
template<const int* pci> struct X {}; int ai[10]; X<ai> xi; // OK: Array-zu-Pointer-Konvertierung und CV-Qualifizierungskonvertierung struct Y {}; template<const Y& b> struct Z {}; Y y; Z<y> z; // OK: Keine Konvertierung template<int (&pa)[5]> struct W {}; int b[5]; W<b> w; // OK: Keine Konvertierung void f(char); void f(int); template<void (*pf)(int)> struct A {}; A<&f> a; // OK: Überladungsauflösung wählt f(int)
template<class T, const char* p> class X {}; X<int, "Studebaker"> x1; // Fehler: String-Literal als Template-Argument template<int* p> class X {}; int a[10]; struct S { int m; static int s; } s; X<&a[2]> x3; // Fehler (bis C++20): Adresse eines Array-Elements X<&s.m> x4; // Fehler (bis C++20): Adresse eines nicht-statischen Members X<&s.s> x5; // OK: Adresse eines statischen Members X<&S::s> x6; // OK: Adresse eines statischen Members template<const int& CRI> struct B {}; B<1> b2; // Fehler: Temporäres Objekt wäre für Template-Argument erforderlich int c = 1; B<c> b1; // OK
Typ-Template-Argumente
Ein Template-Argument für einen Typ-Template-Parameter muss eine Typ-ID sein, die einen unvollständigen Typ benennen kann:
template<typename T> class X {}; // Klassentemplate struct A; // unvollständiger Typ typedef struct {} B; // Typalias für einen unbenannten Typ int main() { X<A> x1; // OK: 'A' bezeichnet einen Typ X<A*> x2; // OK: 'A*' bezeichnet einen Typ X<B> x3; // OK: 'B' bezeichnet einen Typ }
Template-Template-Argumente
Ein Template-Argument für einen Template-Template-Parameter muss ein id-expression sein, das ein Klassentemplate oder ein Template-Alias benennt.
Wenn das Argument eine Klassenvorlage ist, wird nur die primäre Vorlage bei der Übereinstimmung mit dem Parameter berücksichtigt. Die partiellen Spezialisierungen, falls vorhanden, werden nur berücksichtigt, wenn eine Spezialisierung basierend auf diesem Vorlagenvorlagenparameter instanziiert wird.
template<typename T> // Primäres Template class A { int x; }; template<typename T> // Partielle Spezialisierung class A<T*> { long x; }; // Klassentemplate mit einem Template-Template-Parameter V template<template<typename> class V> class C { V<int> y; // verwendet das primäre Template V<int*> z; // verwendet die partielle Spezialisierung }; C<A> c; // c.y.x hat Typ int, c.z.x hat Typ long
Um ein Template-Template-Argument
A
an einen Template-Template-Parameter
P
anzupassen, muss
P
mindestens ebenso spezialisiert
sein wie
A
(siehe unten).
Wenn die Parameterliste von
P
ein
Parameterpaket
enthält, werden null oder mehr Template-Parameter (oder Parameterpakete) aus der Template-Parameterliste von
A
damit abgeglichen.
(seit C++11)
Formal ist ein Template-Template-Parameter
P
mindestens so spezialisiert
wie ein Template-Template-Argument
A
, wenn unter der folgenden Umformulierung in zwei Funktions-Templates das Funktions-Template, das
P
entspricht, mindestens so spezialisiert ist wie das Funktions-Template, das
A
entspricht, gemäß den Partiellen Ordnungsregeln für
Funktions-Templates
. Gegeben eine erfundene Klassen-Template
X
mit der Template-Parameterliste von
A
(einschließlich Standardargumenten):
-
Jede der beiden Funktionsschablonen hat dieselben Template-Parameter wie
Pbzw.A. -
Jede Funktionsschablone hat einen einzelnen Funktionsparameter, dessen Typ eine Spezialisierung von
Xmit Template-Argumenten ist, die den Template-Parametern der jeweiligen Funktionsschablone entsprechen, wobei für jeden Template-ParameterPPin der Template-Parameterliste der Funktionsschablone ein entsprechendes Template-ArgumentAAgebildet wird. WennPPein Parameterpaket deklariert, dann istAAdie PaketentfaltungPP...; andernfalls (seit C++11)AAder Id-AusdruckPP.
Wenn die Neuschreibung einen ungültigen Typ erzeugt, dann ist
P
nicht mindestens so spezialisiert wie
A
.
template<typename T> struct eval; // Primäres Template template<template<typename, typename...> class TT, typename T1, typename... Rest> struct eval<TT<T1, Rest...>> {}; // Partielle Spezialisierung von eval template<typename T1> struct A; template<typename T1, typename T2> struct B; template<int N> struct C; template<typename T1, int N> struct D; template<typename T1, typename T2, int N = 17> struct E; eval<A<int>> eA; // OK: passt zur partiellen Spezialisierung von eval eval<B<int, float>> eB; // OK: passt zur partiellen Spezialisierung von eval eval<C<17>> eC; // Fehler: C passt nicht zu TT in der partiellen Spezialisierung // weil TT's erster Parameter ein // Typ-Template-Parameter ist, während 17 keinen Typ benennt eval<D<int, 17>> eD; // Fehler: D passt nicht zu TT in der partiellen Spezialisierung // weil TT's zweiter Parameter ein // Typ-Parameter-Pack ist, während 17 keinen Typ benennt eval<E<int, float>> eE; // Fehler: E passt nicht zu TT in der partiellen Spezialisierung // weil E's dritter (Standard-)Parameter eine Konstante ist
Vor der Annahme von
P0522R0
musste jeder der Template-Parameter von
A
exakt mit den entsprechenden Template-Parametern von
P
übereinstimmen. Dies verhinderte, dass viele sinnvolle Template-Argumente akzeptiert wurden.
Obwohl es sehr früh darauf hingewiesen wurde ( CWG#150 ), wurden die Änderungen bis zur Lösung auf das C++17-Arbeitsdokument angewendet und die Lösung wurde de facto zu einem C++17-Feature. Viele Compiler deaktivieren es standardmäßig:
- GCC deaktiviert es standardmäßig in allen Sprachmodi vor C++17, es kann in diesen Modi nur durch Setzen eines Compiler-Flags aktiviert werden.
- Clang deaktiviert es standardmäßig in allen Sprachmodi, es kann nur durch Setzen eines Compiler-Flags aktiviert werden.
- Microsoft Visual Studio behandelt es als normale C++17-Funktion und aktiviert es nur in C++17 und späteren Sprachmodi (d.h. keine Unterstützung im C++14-Sprachmodus, welcher der Standardmodus ist).
template<class T> class A { /* ... */ }; template<class T, class U = T> class B { /* ... */ }; template<class... Types> class C { /* ... */ }; template<template<class> class P> class X { /* ... */ }; X<A> xa; // OK X<B> xb; // OK nach P0522R0 // Fehler vorher: keine exakte Übereinstimmung X<C> xc; // OK nach P0522R0 // Fehler vorher: keine exakte Übereinstimmung template<template<class...> class Q> class Y { /* ... */ }; Y<A> ya; // OK Y<B> yb; // OK Y<C> yc; // OK template<auto n> class D { /* ... */ }; // Hinweis: C++17 template<template<int> class R> class Z { /* ... */ }; Z<D> zd; // OK nach P0522R0: Der Template-Parameter // ist spezialisierter als das Template-Argument template<int> struct SI { /* ... */ }; template<template<auto> class> void FA(); // Hinweis: C++17 FA<SI>(); // Fehler
Template-Argument-Äquivalenz
Template-Argument-Äquivalenz wird verwendet, um zu bestimmen, ob zwei Template-Identifikatoren gleich sind.
Zwei Werte sind template-argument-equivalent wenn sie vom selben Typ sind und eine der folgenden Bedingungen erfüllt ist:
- Sie sind vom ganzzahligen oder Aufzählungstyp und ihre Werte sind gleich.
- Sie sind vom Zeigertyp und haben denselben Zeigerwert.
- Sie sind vom Zeiger-auf-Mitglied-Typ und verweisen auf dasselbe Klassenmitglied oder sind beide der Null-Mitglied-Zeigerwert.
- Sie sind vom Lvalue-Referenztyp und verweisen auf dasselbe Objekt oder dieselbe Funktion.
|
(seit C++11) |
|
(seit C++20) |
Auflösung von Mehrdeutigkeiten
Wenn ein Template-Argument sowohl als type-id als auch als Ausdruck interpretiert werden kann, wird es stets als type-id interpretiert, selbst wenn der entsprechende Template-Parameter konstant ist:
template<class T> void f(); // #1 template<int I> void f(); // #2 void g() { f<int()>(); // "int()" ist sowohl ein Typ als auch ein Ausdruck, // ruft #1 auf, da es als Typ interpretiert wird }
Hinweise
Vor C++26 wurden konstante Template-Argumente in der Standardformulierung als Nicht-Typ-Template-Argumente bezeichnet. Die Terminologie wurde geändert durch P2841R6 / PR #7587 .
| Feature-Test-Makro | Wert | Std | Feature |
|---|---|---|---|
__cpp_template_template_args
|
201611L
|
(C++17)
(DR) |
Matching von Template-Template-Argumenten |
__cpp_nontype_template_args
|
201411L
|
(C++17) | Erlaubt konstante Auswertung für alle konstanten Template-Argumente |
201911L
|
(C++20) | Klassentypen und Gleitkommatypen in konstanten Template-Parametern |
Beispiel
|
Dieser Abschnitt ist unvollständig
Grund: Kein Beispiel |
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 150
( P0522R0 ) |
C++98 |
Template-Template-Argumente mussten Parameterlisten
von Template-Template-Parametern exakt entsprechen |
Spezialisiertere ebenfalls
erlaubt |
| CWG 354 | C++98 | Nullzeiger-Werte konnten keine konstanten Template-Argumente sein | erlaubt |
| CWG 1398 | C++11 |
Konstante Template-Argumente konnten nicht den Typ
std::nullptr_t
haben
|
erlaubt |
| CWG 1570 | C++98 | Konstante Template-Argumente konnten Adressen von Subobjekten bezeichnen | nicht erlaubt |
| P2308R1 |
C++11
C++20 |
1. Listeninitialisierung war nicht erlaubt für
konstante Template-Argumente (C++11) 2. Es war unklar, wie konstante Template- Parameter von Klassentypen initialisiert werden (C++20) |
1. erlaubt
2. klargestellt |