Namespaces
Variants

Template arguments

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

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 T einen Platzhaltertyp enthält oder ein Platzhalter für einen abgeleiteten Klassentyp ist, ist der Typ des Template-Parameters der für die Variable x in der erfundenen Deklaration T x = E ; abgeleitete Typ.

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)
  • Wenn A ein Ausdruck ist:
  • Andernfalls ( A ist eine geschweifte Klammer-Initialisierungsliste) wird eine temporäre Variable constexpr T v = A ; eingeführt. Der Wert von P ist der von v .
  • Die Lebensdauer von v endet unmittelbar nach ihrer Initialisierung.
(seit C++11)
(bis C++20)
  • Wenn T kein Klassentyp ist und A ein Ausdruck ist:
  • Andernfalls ( T ist ein Klassentyp oder A ist eine geschweifte Klammer-Initialisierungsliste) wird eine temporäre Variable constexpr T v = A ; eingeführt.
  • Die Lebensdauer von v endet unmittelbar nach ihrer Initialisierung und der von P .
  • Wenn die Initialisierung von P eine der folgenden Bedingungen erfüllt, ist das Programm fehlerhaft:
  • Andernfalls ist der Wert von P der von v .
(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:

  • Für integrale und arithmetische Typen muss das bei der Instanziierung angegebene Template-Argument ein konvertierter konstanter Ausdruck des Template-Parametertyps sein (sodass bestimmte implizite Konvertierungen angewendet werden).
  • Für Zeiger auf Objekte müssen die Template-Argumente die Adresse eines vollständigen Objekts mit statischer Speicherdauer und einer Verknüpfung (entweder intern oder extern) bezeichnen, oder ein konstanter Ausdruck, der zum entsprechenden Nullzeiger oder std::nullptr_t (seit C++11) ausgewertet wird.
  • Für Zeiger auf Funktionen sind gültige Argumente Zeiger auf Funktionen mit Verknüpfung (oder konstante Ausdrücke, die zu Nullzeigerwerten ausgewertet werden).
  • Für Lvalue-Referenzparameter darf das bei der Instanziierung angegebene Argument kein temporäres Objekt, ein unbenannter Lvalue oder ein benannter Lvalue ohne Verknüpfung sein (mit anderen Worten, das Argument muss eine Verknüpfung haben).
  • Für Zeiger auf Member muss das Argument ein Zeiger auf Member sein, ausgedrückt als & Class :: Member oder ein konstanter Ausdruck, der zu einem Nullzeiger oder std::nullptr_t (seit C++11) ausgewertet wird.

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

  • einem temporären Objekt (einschließlich solcher, die während der Referenzinitialisierung erstellt werden);
  • einem String-Literal ;
  • dem Ergebnis von typeid ;
  • der vordefinierten Variable __func__ ;
  • oder einem Unterobjekt (einschließlich nicht-statischer Klassenmember, Basis-Unterobjekte oder Array-Elemente) eines der oben genannten (seit C++20) .
(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 P bzw. A .
  • Jede Funktionsschablone hat einen einzelnen Funktionsparameter, dessen Typ eine Spezialisierung von X mit Template-Argumenten ist, die den Template-Parametern der jeweiligen Funktionsschablone entsprechen, wobei für jeden Template-Parameter PP in der Template-Parameterliste der Funktionsschablone ein entsprechendes Template-Argument AA gebildet wird. Wenn PP ein Parameterpaket deklariert, dann ist AA die Paketentfaltung PP... ; andernfalls (seit C++11) AA der Id-Ausdruck PP .

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)
  • Sie sind vom Gleitkommatyp und ihre Werte sind identisch.
  • Sie sind vom Array-Typ (in diesem Fall müssen die Arrays Mitgliedsobjekte einer Klasse/Union sein) und ihre entsprechenden Elemente sind template-argument-equivalent.
  • Sie sind vom Union-Typ und entweder haben beide kein aktives Mitglied oder sie haben dasselbe aktive Mitglied und ihre aktiven Mitglieder sind template-argument-equivalent.
  • Sie sind von einem Lambda-Abschlusstyp.
  • Sie sind vom Nicht-Union-Klassentyp und ihre entsprechenden direkten Unterobjekte und Referenzmitglieder sind template-argument-equivalent.
(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

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