Template parameters
Jede Template wird durch einen oder mehrere Template-Parameter parametrisiert.
Jeder Parameter in der template-parameter-list (siehe template declaration syntax ) gehört zu einer der folgenden Kategorien:
- konstanter Template-Parameter
- Typ-Template-Parameter
- Template-Template-Parameter
Inhaltsverzeichnis |
Konstanter Template-Parameter
Auch bekannt als non-type template parameter (siehe unten ).
| Typ Name (optional) | (1) | ||||||||
Typ
Name
(optional)
=
Standardwert
|
(2) | ||||||||
Typ
...
Name
(optional)
|
(3) | (seit C++11) | |||||||
| type | - |
einer der folgenden Typen:
|
||||
| name | - | der Name des konstanten Template-Parameters | ||||
| default | - | das Standard-Template-Argument |
Ein
struktureller Typ
ist einer der folgenden Typen (optional cv-qualifiziert, die Qualifizierer werden ignoriert):
- Lvalue-Referenztyp (zu Objekt oder zu Funktion);
- ein integraler Typ ;
- ein Zeigertyp (zu Objekt oder zu Funktion);
- ein Zeiger-auf-Mitglied-Typ (zu Mitgliedsobjekt oder zu Mitgliedsfunktion);
- ein Aufzählungstyp ;
| (seit C++11) |
|
(seit C++20) |
Array- und Funktionstypen können in einer Template-Deklaration geschrieben werden, werden jedoch automatisch durch Zeiger auf Objekt und Zeiger auf Funktion entsprechend ersetzt.
Wenn der Name eines konstanten Template-Parameters in einem Ausdruck innerhalb des Rumpfs des Klassentemplates verwendet wird, ist es ein nicht modifizierbarer prvalue , es sei denn, sein Typ war ein Lvalue-Referenztyp , oder es sei denn, sein Typ ist ein Klassentyp (since C++20) .
Ein Template-Parameter der Form
class
Foo
ist kein unbenannter konstanter Template-Parameter vom Typ
Foo
, selbst wenn ansonsten
class
Foo
ein
elaborated type specifier
ist und
class
Foo x
;
x
als vom Typ
Foo
deklariert.
|
Ein
Bezeichner
, der einen konstanten Template-Parameter vom Klassentyp
struct A { friend bool operator==(const A&, const A&) = default; }; template<A a> void f() { &a; // OK const A& ra = a, &rb = a; // Both bound to the same template parameter object assert(&ra == &rb); // passes } |
(seit C++20) |
Typ-Template-Parameter
| type-parameter-key name (optional) | (1) | ||||||||
type-parameter-key
name
(optional)
=
default
|
(2) | ||||||||
type-parameter-key
...
name
(optional)
|
(3) | (seit C++11) | |||||||
| type-constraint name (optional) | (4) | (seit C++20) | |||||||
type-constraint
name
(optional)
=
default
|
(5) | (seit C++20) | |||||||
type-constraint
...
name
(optional)
|
(6) | (seit C++20) | |||||||
| type-parameter-key | - |
entweder
typename
oder
class
. Es gibt keinen Unterschied zwischen diesen Schlüsselwörtern in einer Typ-Template-Parameterdeklaration
|
| type-constraint | - | entweder der Name eines Konzepts oder der Name eines Konzepts gefolgt von einer Liste von Template-Argumenten (in spitzen Klammern). In beiden Fällen kann der Konzeptname optional qualifiziert sein |
| name | - | der Name des Typ-Template-Parameters |
| default | - | das Standard-Template-Argument |
template<class T> class My_vector { /* ... */ };
template<class T = void> struct My_op_functor { /* ... */ };
template<My_concept T> class My_constrained_vector { /* ... */ };
template<My_concept T = void> class My_constrained_op_functor { /* ... */ };
template<My_concept... Ts> class My_constrained_tuple { /* ... */ };
Der Name des Parameters ist optional:
// Deklarationen der oben gezeigten Templates: template<class> class My_vector; template<class = void> struct My_op_functor; template<typename...> class My_tuple;
Im Rumpf der Template-Deklaration ist der Name eines Typparameters ein Typalias-Name, der den Typ referenziert, der bei der Instanziierung des Templates angegeben wird.
|
Jeder eingeschränkte Parameter
template<typename T> concept C1 = true; template<typename... Ts> // variadic concept concept C2 = true; template<typename T, typename U> concept C3 = true; template<C1 T> struct s1; // constraint-expression is C1<T> template<C1... T> struct s2; // constraint-expression is (C1<T> && ...) template<C2... T> struct s3; // constraint-expression is (C2<T> && ...) template<C3<int> T> struct s4; // constraint-expression is C3<T, int> template<C3<int>... T> struct s5; // constraint-expression is (C3<T, int> && ...) |
(seit C++20) |
Template-Template-Parameter
template
<
parameter-list
>
type-parameter-key
name
(optional)
|
(1) | ||||||||
template
<
parameter-list
>
type-parameter-key
name
(optional)
=
default
|
(2) | ||||||||
template
<
parameter-list
>
type-parameter-key
...
name
(optional)
|
(3) | (seit C++11) | |||||||
| type-parameter-key | - |
class
oder
typename
(seit C++17)
|
Im Rumpf der Template-Deklaration ist der Name dieses Parameters ein Template-Name (und benötigt Argumente zur Instanziierung).
template<typename T> class my_array {}; // Zwei Typ-Template-Parameter und ein Template-Template-Parameter: template<typename K, typename V, template<typename> typename C = my_array> class Map { C<K> key; C<V> value; };
Namensauflösung für Template-Parameter
Der Name eines Template-Parameters darf innerhalb seines Gültigkeitsbereichs (einschließlich verschachtelter Gültigkeitsbereiche) nicht erneut deklariert werden. Ein Template-Parameter darf nicht denselben Namen wie der Template-Name haben.
template<class T, int N> class Y { int T; // Fehler: Template-Parameter neu deklariert void f() { char T; // Fehler: Template-Parameter neu deklariert } }; template<class X> class X; // Fehler: Template-Parameter neu deklariert
In der Definition eines Members einer Klassenvorlage, die außerhalb der Klassenvorlagendefinition erscheint, verdeckt der Name eines Members der Klassenvorlage den Namen eines Template-Parameters einer beliebigen umschließenden Klassenvorlage, jedoch nicht einen Template-Parameter des Members, wenn der Member eine Klassen- oder Funktionsvorlage ist.
template<class T> struct A { struct B {}; typedef void C; void f(); template<class U> void g(U); }; template<class B> void A<B>::f() { B b; // A's B, nicht der Template-Parameter } template<class B> template<class C> void A<B>::g(C) { B b; // A's B, nicht der Template-Parameter C c; // der Template-Parameter C, nicht A's C }
In der Definition eines Members einer Klassenvorlage, die außerhalb des Namensraums erscheint, der die Klassenvorlagendefinition enthält, verdeckt der Name eines Template-Parameters den Namen eines Members dieses Namensraums.
namespace N { class C {}; template<class T> class B { void f(T); }; } template<class C> void N::B<C>::f(C) { C b; // C ist der Template-Parameter, nicht N::C }
In der Definition eines Klassentemplates oder in der Definition eines Members eines solchen Templates, die außerhalb der Templatedefinition erscheint, verdeckt für jede nicht- abhängige Basisklasse, wenn der Name der Basisklasse oder der Name eines Members der Basisklasse mit dem Namen eines Template-Parameters übereinstimmt, der Basisklassenname oder Membername den Template-Parameternamen.
struct A { struct B {}; int C; int Y; }; template<class B, class C> struct X : A { B b; // A's B C b; // Fehler: A's C ist kein Typname };
Standardmäßige Template-Argumente
Standardmäßige Template-Argumente werden in den Parameterlisten nach dem = Zeichen angegeben. Standardwerte können für jede Art von Template-Parameter (Typ, Konstante oder Template) , jedoch nicht für Parameter-Packs (since C++11) spezifiziert werden.
Wenn für einen Template-Parameter eines primären Klassentemplates ein Standardwert angegeben wird , eines primären Variablen-Templates, (seit C++14) muss jeder nachfolgende Template-Parameter ein Standardargument haben , außer der letzte kann ein Template-Parameterpaket sein (seit C++11) . In einem Funktions-Template gibt es keine Einschränkungen für Parameter, die einem Standardwert folgen , und ein Parameterpaket kann nur dann von weiteren Typparametern gefolgt werden, wenn diese Standardwerte haben oder von den Funktionsargumenten abgeleitet werden können (seit C++11) .
Standardparameter sind nicht erlaubt
- in der außerhalb der Klasse befindlichen Definition eines Members einer Klassenvorlage (diese müssen in der Deklaration innerhalb des Klassenkörpers bereitgestellt werden). Beachten Sie, dass Membervorlagen von Nicht-Vorlagenklassen Standardparameter in ihren außerhalb der Klasse befindlichen Definitionen verwenden können (siehe GCC Bug 53856 )
- in friend-Klassenvorlagen Deklarationen
|
(bis C++11) |
|
Bei einer Friend-Funktionstemplate-Deklaration sind Standard-Template-Argumente nur zulässig, wenn die Deklaration eine Definition ist und keine weiteren Deklarationen dieser Funktion in dieser Übersetzungseinheit auftreten. |
(since C++11) |
Standardmäßige Template-Argumente, die in den Deklarationen erscheinen, werden ähnlich wie Standard-Funktionsargumente zusammengeführt:
template<typename T1, typename T2 = int> class A; template<typename T1 = int, typename T2> class A; // die obigen Zeilen entsprechen der folgenden: template<typename T1 = int, typename T2 = int> class A;
Derselbe Parameter kann jedoch nicht zweimal in demselben Gültigkeitsbereich mit Standardargumenten versehen werden:
template<typename T = int> class X; template<typename T = int> class X {}; // Fehler
Beim Parsen eines Standard-Template-Arguments für einen konstanten Template-Parameter wird das erste nicht-geschachtelte > als Ende der Template-Parameterliste interpretiert und nicht als Größer-als-Operator:
template<int i = 3 > 4> // Syntaxfehler class X { /* ... */ }; template<int i = (3 > 4)> // OK class Y { /* ... */ };
Die Template-Parameterlisten von Template-Template-Parametern können ihre eigenen Standardargumente haben, die nur wirksam sind, wo der Template-Template-Parameter selbst im Gültigkeitsbereich ist:
// Klassentemplate mit einem Typ-Template-Parameter mit einem Standardwert template<typename T = float> struct B {}; // Template-Template-Parameter T hat eine Parameterliste, die // aus einem Typ-Template-Parameter mit einem Standardwert besteht template<template<typename = float> typename T> struct A { void f(); void g(); }; // Out-of-Body-Memberfunktions-Template-Definitionen template<template<typename TT> class T> void A<T>::f() { T<> t; // Fehler: TT hat keinen Standardwert im Gültigkeitsbereich } template<template<typename TT = char> class T> void A<T>::g() { T<> t; // OK: t ist T<char> }
Memberenzugriff für die in einem Standard-Template-Parameter verwendeten Namen wird bei der Deklaration überprüft, nicht an der Stelle der Verwendung:
class B {}; template<typename T> class C { protected: typedef T TT; }; template<typename U, typename V = typename U::TT> class D: public U {}; D<C<B>>* d; // Fehler: C::TT ist geschützt
|
Das Standard-Template-Argument wird implizit instanziiert, wenn der Wert dieses Standardarguments benötigt wird, außer wenn das Template zur Benennung einer Funktion verwendet wird: template<typename T, typename U = int> struct S {}; S<bool>* p; // The default argument for U is instantiated at this point // the type of p is S<bool, int>* |
(since C++14) |
Hinweise
Vor C++26 wurden konstante Template-Parameter in der Standardformulierung als Nicht-Typ-Template-Parameter bezeichnet. Die Terminologie wurde geändert durch P2841R6 / PR#7587 .
|
In Template-Parametern können Typ-Einschränkungen sowohl für Typ- als auch für Konstanten-Parameter verwendet werden, abhängig davon, ob auto vorhanden ist. template<typename> concept C = true; template<C, // type parameter C auto // constant parameter > struct S{}; S<int, 0> s;
|
(since C++20) |
| Feature-Test-Makro | Wert | Std | Feature |
|---|---|---|---|
__cpp_nontype_template_parameter_auto
|
201606L
|
(C++17) | Deklaration von konstanten Template-Parametern mit auto |
__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 |
Beispiele
#include <array> #include <iostream> #include <numeric> // einfacher konstanter Template-Parameter template<int N> struct S { int a[N]; }; template<const char*> struct S2 {}; // komplexes Konstanten-Beispiel template < char c, // integraler Typ int (&ra)[5], // Lvalue-Referenz auf Objekt (vom Array-Typ) int (*pf)(int), // Zeiger auf Funktion int (S<10>::*a)[10] // Zeiger auf Member-Objekt (vom Typ int[10]) > struct Complicated { // ruft die zur Kompilierzeit ausgewählte Funktion auf // und speichert das Ergebnis im zur Kompilierzeit ausgewählten Array void foo(char base) { ra[4] = pf(c - base); } }; // S2<"fail"> s2; // Fehler: Zeichenkette kann nicht verwendet werden char okay[] = "okay"; // statisches Objekt mit Verknüpfung // S2<&okay[0]> s3; // Fehler: Array-Element hat keine Verknüpfung S2<okay> s4; // funktioniert int a[5]; int f(int n) { return n; } // C++20: NTTP kann ein Literal-Klassentyp sein template<std::array arr> constexpr auto sum() { return std::accumulate(arr.cbegin(), arr.cend(), 0); } // C++20: Klassen-Template-Argumente werden am Aufrufort abgeleitet static_assert(sum<std::array<double, 8>{3, 1, 4, 1, 5, 9, 2, 6}>() == 31.0); // C++20: NTTP-Argumentableitung und CTAD static_assert(sum<std::array{2, 7, 1, 8, 2, 8}>() == 28); int main() { S<10> s; // s.a ist ein Array von 10 int s.a[9] = 4; Complicated<'2', a, f, &S<10>::a> c; c.foo('0'); std::cout << s.a[9] << a[4] << '\n'; }
Ausgabe:
42
|
Dieser Abschnitt ist unvollständig
Grund: Weitere Beispiele benötigt |
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 184 | C++98 |
ob Template-Parameter von Template-Template-Parametern
Standardargumente haben dürfen, war nicht spezifiziert |
Spezifikation hinzugefügt |
| CWG 1922 | C++98 |
es war unklar, ob ein Klassentemplate, dessen Name ein
injizierter Klassenname ist, Standardargumente aus früheren Deklarationen verwenden kann |
erlaubt |
| CWG 2032 | C++14 |
für Variablen-Templates gab es keine Einschränkung für die Template-Parameter
nach einem Template-Parameter mit einem Standardargument |
dieselbe Einschränkung anwenden
wie bei Klassentemplates und Alias-Templates |
| CWG 2542 | C++20 | es war unklar, ob der Closure-Typ strukturell ist | er ist nicht strukturell |
| CWG 2845 | C++20 | der Closure-Typ war nicht strukturell |
er ist strukturell
wenn erfanglos |