Namensräume
Varianten
Aktionen

Template-Parameter und Template-Argumente

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
 
 
 
 

Inhalt

[bearbeiten] Template-Parameter

Jedes Template wird durch einen oder mehrere Template-Parameter parametrisiert, die in der parameter-list der Template-Deklarationssyntax angegeben sind.

template < parameter-list > declaration (1)
template < parameter-list > requires constraint declaration (2) (seit C++20)

Jeder Parameter in parameter-list kann sein

  • ein Nicht-Typ-Template-Parameter;
  • ein Typ-Template-Parameter;
  • ein Template-Template-Parameter.


[bearbeiten] Nicht-Typ-Template-Parameter

type name (optional) (1)
type name (optional) = default (2)
type ... name (optional) (3) (seit C++11)
1) Ein Nicht-Typ-Template-Parameter.
2) Ein Nicht-Typ-Template-Parameter mit einem Standard-Template-Argument.
3) Ein Nicht-Typ-Template-Parameterpack.
type - einer der folgenden Typen
  • ein struktureller Typ (siehe unten)
(seit C++17)
(seit C++20)
name - der Name des Nicht-Typ-Template-Parameters
default - das Standard-Template-Argument

Ein struktureller Typ ist einer der folgenden Typen (optional cv-qualifiziert, die Qualifizierer werden ignoriert)

(seit C++11)
  • alle Basisklassen und nicht-statischen Datenmember sind öffentlich und nicht veränderbar und
  • die Typen aller Basisklassen und nicht-statischen Datenmember sind strukturelle Typen oder (möglicherweise mehrdimensionale) Arrays davon.
(seit C++20)

Array- und Funktionstypen können in einer Template-Deklaration geschrieben werden, werden aber automatisch durch Zeiger auf Objekte und Zeiger auf Funktionen ersetzt, je nach Fall.

Wenn der Name eines Nicht-Typ-Template-Parameters in einem Ausdruck innerhalb des Körpers einer Klassenvorlage verwendet wird, ist er ein unveränderbares prvalue, es sei denn, sein Typ war ein Lvalue-Referenztyp, oder es sei denn, sein Typ ist ein Klassentyp(seit C++20).

Ein Template-Parameter der Form class Foo ist kein unbenannter Nicht-Typ-Template-Parameter vom Typ Foo, auch wenn class Foo ansonsten ein elaborierter Typspezifizierer ist und class Foo x; x vom Typ Foo deklariert.

Ein Bezeichner, der einen Nicht-Typ-Template-Parameter vom Klassentyp T benennt, bezeichnet ein Objekt mit statischer Speicherdauer vom Typ const T, genannt Template-Parameter-Objekt, das template-argument-äquivalent zu dem entsprechenden Template-Argument ist, nachdem dieses in den Typ des Template-Parameters konvertiert wurde. Kein paar Template-Parameter-Objekte sind template-argument-äquivalent.

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)

[bearbeiten] 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-Parameter-Deklaration.
type-constraint - entweder der Name eines Konzepts oder der Name eines Konzepts gefolgt von einer Liste von Template-Argumenten (in spitzen Klammern). In jedem Fall kann der Konzeptname optional qualifiziert sein.
name - der Name des Typ-Template-Parameters
default - das Standard-Template-Argument
1) Ein Typ-Template-Parameter ohne Standardwert.
template<class T>
class My_vector { /* ... */ };
2) Ein Typ-Template-Parameter mit einem Standardwert.
template<class T = void>
struct My_op_functor { /* ... */ };
3) Ein Typ-Template-Parameterpack.
template<typename... Ts>
class My_tuple { /* ... */ };
4) Ein eingeschränkter Typ-Template-Parameter ohne Standardwert.
template<My_concept T>
class My_constrained_vector { /* ... */ };
5) Ein eingeschränkter Typ-Template-Parameter mit einem Standardwert.
template<My_concept T = void>
class My_constrained_op_functor { /* ... */ };
6) Ein eingeschränkter Typ-Template-Parameterpack.
template<My_concept... Ts>
class My_constrained_tuple { /* ... */ };

Der Name des Parameters ist optional.

// Declarations of the templates shown above:
template<class>
class My_vector;
template<class = void>
struct My_op_functor;
template<typename...>
class My_tuple;

Im Körper der Template-Deklaration ist der Name eines Typ-Parameters ein typedef-name, der auf den Typ aliasen, der bei der Instanziierung des Templates bereitgestellt wird.

Jeder eingeschränkte Parameter P, dessen type-constraint Q das Konzept C bezeichnet, führt einen Constraint-Ausdruck E gemäß den folgenden Regeln ein:

  • wenn Q C ist (ohne Argumentliste),
  • wenn P kein Parameterpack ist, ist E einfach C<P>
  • andernfalls ist P ein Parameterpack, E ist ein Fold-Ausdruck (C<P> && ...)
  • wenn Q C<A1,A2...,AN> ist, dann ist E C<P,A1,A2,...AN> oder (C<P,A1,A2,...AN> && ...), beziehungsweise.
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)

[bearbeiten] 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)
1) Ein Template-Template-Parameter mit einem optionalen Namen.
2) Ein Template-Template-Parameter mit einem optionalen Namen und einem Standardwert.
3) Ein Template-Template-Parameterpack mit einem optionalen Namen.

Im Körper der Template-Deklaration ist der Name dieses Parameters ein Template-Name (und benötigt Argumente zur Instanziierung).

template<typename T>
class my_array {};
 
// two type template parameters and one template template parameter:
template<typename K, typename V, template<typename> typename C = my_array>
class Map
{
    C<K> key;
    C<V> value;
};

[bearbeiten] Namensauflösung für Template-Parameter

Der Name eines Template-Parameters darf in seinem Geltungsbereich (einschließlich verschachtelter Geltungsbereiche) 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;      // error: template parameter redeclared
    void f()
    {
        char T; // error: template parameter redeclared
    }
};
 
template<class X>
class X; // error: template parameter redeclared

In der Definition eines Mitglieds einer Klassenvorlage, die außerhalb der Klassenvorlagen-Definition erscheint, überschattet der Name eines Mitglieds der Klassenvorlage den Namen eines Template-Parameters von umschließenden Klassenvorlagen, aber nicht den eines Template-Parameters des Mitglieds, wenn das Mitglied 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, not the template parameter
}
 
template<class B>
template<class C>
void A<B>::g(C)
{
    B b; // A's B, not the template parameter
    C c; // the template parameter C, not A's C
}

In der Definition eines Mitglieds einer Klassenvorlage, die außerhalb des Namespaces erscheint, der die Klassenvorlagen-Definition enthält, überschattet der Name eines Template-Parameters den Namen eines Mitglieds dieses Namespaces.

namespace N
{
    class C {};
 
    template<class T>
    class B
    {
        void f(T);
    };
}
 
template<class C>
void N::B<C>::f(C)
{
    C b; // C is the template parameter, not N::C
}

In der Definition einer Klassenvorlage oder in der Definition eines Mitglieds einer solchen Vorlage, die außerhalb der Vorlagen-Definition erscheint, gilt für jede nicht-abhängige Basisklasse: Wenn der Name der Basisklasse oder der Name eines Mitglieds der Basisklasse mit dem Namen eines Template-Parameters übereinstimmt, überschattet der Basisklassenname oder Mitgliedsname 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; // error: A's C isn't a type name
};

[bearbeiten] Template-Argumente

Damit ein Template instanziiert werden kann, muss jeder Template-Parameter (Typ, Nicht-Typ oder Template) durch ein entsprechendes Template-Argument ersetzt werden. Für Klassenvorlagen werden die Argumente entweder explizit bereitgestellt, aus dem Initialisierer abgeleitet, (seit C++17) oder standardmäßig gesetzt. Für Funktionsvorlagen werden die Argumente explizit bereitgestellt, aus dem Kontext abgeleitet oder standardmäßig gesetzt.

Wenn ein Argument sowohl als type-id als auch als Ausdruck interpretiert werden kann, wird es immer als type-id interpretiert, auch wenn der entsprechende Template-Parameter ein Nicht-Typ ist.

template<class T>
void f(); // #1
 
template<int I>
void f(); // #2
 
void g()
{
    f<int()>(); // "int()" is both a type and an expression,
                // calls #1 because it is interpreted as a type
}

[bearbeiten] Nicht-Typ-Template-Argumente

Das Template-Argument, das mit einem Nicht-Typ-Template-Parameter verwendet werden kann, kann jeder offensichtlich konstanten Auswertung unterzogene Ausdruck sein.

(bis C++11)

Das Template-Argument, das mit einem Nicht-Typ-Template-Parameter verwendet werden kann, kann jede Initialisiererklausel sein. Wenn die Initialisiererklausel ein Ausdruck ist, muss sie offensichtlich konstanten Auswertung unterzogen werden.

(seit C++11)

Gegeben den Typ der Nicht-Typ-Template-Parameter-Deklaration 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 Variablen mit statischer Speicherdauer erfüllen.

(seit C++26)

Wenn T einen Platzhaltertyp enthält oder ein Platzhalter für einen abgeleiteten Klassentyp ist, ist der Typ des Template-Parameters der Typ, der für die Variable x in der erfundenen Deklaration T x = E; abgeleitet wird.

Wenn ein abgeleiteter Parametertyp kein struktureller Typ ist, ist das Programm fehlerhaft.

Bei Nicht-Typ-Template-Parameterpaketen, 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: non-type template parameter type is int
B<'a'> b2; // OK: non-type template parameter type is char
B<2.5> b3; // error (until C++20): non-type template parameter type cannot be double
 
// C++20 deduced class type placeholder, class template arguments are deduced at the
// call site
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 Nicht-Typ-Template-Parameters P vom (möglicherweise abgeleitet)(seit 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 in geschweifte Klammern eingeschlossene 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 in geschweifte Klammern eingeschlossene 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; // error: initialization of the template parameter object
            //        is not a constant expression
 
struct J2
{
    J2 *self = this;
    constexpr J2() {}
    constexpr J2(const J2&) {}
};
 
B<J2{}> j2; // error: the template parameter object is not
            //        template-argument-equivalent to introduced temporary

Die folgenden Einschränkungen gelten beim Instanziieren von Templates, die Nicht-Typ-Template-Parameter haben:

  • Für Integral- und Arithmetiktypen muss das während der Instanziierung bereitgestellte Template-Argument ein konvertierter konstanter Ausdruck des Typs des Template-Parameters sein (daher wird eine bestimmte implizite Konvertierung angewendet).
  • Für Zeiger auf Objekte müssen die Template-Argumente die Adresse eines vollständigen Objekts mit statischer Speicherdauer und Bindung (intern oder extern) bezeichnen oder ein konstanter Ausdruck, der zu dem entsprechenden Nullzeiger oder std::nullptr_t(seit C++11)-Wert ausgewertet wird.
  • Für Zeiger auf Funktionen sind die gültigen Argumente Zeiger auf Funktionen mit Bindung (oder konstante Ausdrücke, die zu Nullzeigerwerten ausgewertet werden).
  • Für Lvalue-Referenzparameter kann das bei der Instanziierung bereitgestellte Argument kein temporäres Objekt, kein unbenanntes Lvalue oder kein benanntes Lvalue ohne Bindung sein (mit anderen Worten, das Argument muss eine Bindung haben).
  • Für Zeiger auf Mitglieder muss das Argument ein Zeiger auf ein Mitglied sein, ausgedrückt als &Class::Member oder ein konstanter Ausdruck, der zu einem Nullzeiger oder std::nullptr_t(seit C++11)-Wert ausgewertet wird.

Insbesondere impliziert dies, dass Zeichenkettenliterale, Adressen von Array-Elementen und Adressen von nicht-statischen Mitgliedern nicht als Template-Argumente verwendet werden können, um Templates zu instanziieren, deren entsprechende Nicht-Typ-Template-Parameter Zeiger auf Objekte sind.

(bis C++17)

Nicht-Typ-Template-Parameter vom Referenz- oder Zeigertyp und nicht-statische Datenmember vom Referenz- oder Zeigertyp in einem Nicht-Typ-Template-Parameter vom Klassentyp und dessen Unterobjekten(seit C++20) dürfen nicht verweisen auf/die Adresse von

  • ein temporäres Objekt (einschließlich eines während der Referenzinitialisierung erstellten);
  • ein Zeichenkettenliteral;
  • das Ergebnis von typeid;
  • die vordefinierte Variable __func__;
  • oder ein Unterobjekt (einschließlich nicht-statisches Klassenmitglied, Basisunterobjekt oder Array-Element) eines der obigen(seit C++20).
(seit C++17)
template<const int* pci>
struct X {};
 
int ai[10];
X<ai> xi; // OK: array to pointer conversion and cv-qualification conversion
 
struct Y {};
 
template<const Y& b>
struct Z {};
 
Y y;
Z<y> z;   // OK: no conversion
 
template<int (&pa)[5]>
struct W {};
 
int b[5];
W<b> w;   // OK: no conversion
 
void f(char);
void f(int);
 
template<void (*pf)(int)>
struct A {};
 
A<&f> a;  // OK: overload resolution selects f(int)
template<class T, const char* p>
class X {};
 
X<int, "Studebaker"> x1; // error: string literal as template-argument
 
template<int* p>
class X {};
 
int a[10];
 
struct S
{
    int m;
    static int s;
} s;
 
X<&a[2]> x3; // error (until C++20): address of array element
X<&s.m> x4;  // error (until C++20): address of non-static member
X<&s.s> x5;  // OK: address of static member
X<&S::s> x6; // OK: address of static member
 
template<const int& CRI>
struct B {};
 
B<1> b2;     // error: temporary would be required for template argument
int c = 1;
B<c> b1;     // OK

[bearbeiten] Typ-Template-Argumente

Ein Template-Argument für einen Typ-Template-Parameter muss ein Typ-Bezeichner sein, der einen unvollständigen Typ benennen kann.

template<typename T>
class X {}; // class template
 
struct A;            // incomplete type
typedef struct {} B; // type alias to an unnamed type
 
int main()
{
    X<A> x1;  // OK: 'A' names a type
    X<A*> x2; // OK: 'A*' names a type
    X<B> x3;  // OK: 'B' names a type
}

[bearbeiten] Template-Template-Argumente

Ein Template-Argument für einen Template-Template-Parameter muss ein id-Ausdruck sein, der eine Klassenvorlage oder ein Template-Alias benennt.

Wenn das Argument eine Klassenvorlage ist, wird nur die primäre Vorlage bei der Zuordnung zum Parameter berücksichtigt. Die partiellen Spezialisierungen, falls vorhanden, werden nur berücksichtigt, wenn eine Spezialisierung auf Basis dieser Template-Template-Parameter instanziiert wird.

template<typename T> // primary template
class A { int x; };
 
template<typename T> // partial specialization
class A<T*> { long x; };
 
// class template with a template template parameter V
template<template<typename> class V>
class C
{
    V<int> y;  // uses the primary template
    V<int*> z; // uses the partial specialization
};
 
C<A> c; // c.y.x has type int, c.z.x has type long

Um ein Template-Template-Argument A einem Template-Template-Parameter P zuzuordnen, muss P mindestens so spezialisiert sein wie A (siehe unten). Wenn Ps Parameterliste einen Parameterpack enthält, werden null oder mehr Template-Parameter (oder Parameterpacks) aus der Template-Parameterliste von A durch ihn abgeglichen.(seit C++11)

Formal ist ein Template-Template-Parameter P mindestens so spezialisiert wie ein Template-Template-Argument A, wenn, unter Berücksichtigung der folgenden Umschreibung in zwei Funktionsvorlagen, die Funktionsvorlage, die P entspricht, mindestens so spezialisiert ist wie die Funktionsvorlage, die A entspricht, gemäß den Regeln der partiellen Ordnung für Funktionsvorlagen. Gegeben eine erfundene Klassenvorlage X mit der Template-Parameterliste von A (einschließlich Standardargumenten):

  • Jede der beiden Funktionsvorlagen hat dieselben Template-Parameter wie P bzw. A.
  • Jede Funktionsvorlage hat einen einzelnen Funktionsparameter, dessen Typ eine Spezialisierung von X mit Template-Argumenten ist, die den Template-Parametern der jeweiligen Funktionsvorlage entsprechen, wobei für jeden Template-Parameter PP in der Template-Parameterliste der Funktionsvorlage ein entsprechender Template-Parameter AA gebildet wird. Wenn PP ein Parameterpack deklariert, dann ist AA die Pack-Expansion PP...; andernfalls(seit C++11) ist AA der id-Ausdruck PP.

Wenn die Umschreibung einen ungültigen Typ ergibt, dann ist P nicht mindestens so spezialisiert wie A.

template<typename T>
struct eval;                     // primary template
 
template<template<typename, typename...> class TT, typename T1, typename... Rest>
struct eval<TT<T1, Rest...>> {}; // partial specialization of 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: matches partial specialization of eval
eval<B<int, float>> eB; // OK: matches partial specialization of eval
eval<C<17>> eC;         // error: C does not match TT in partial specialization
                        // because TT's first parameter is a
                        // type template parameter, while 17 does not name a type
eval<D<int, 17>> eD;    // error: D does not match TT in partial specialization
                        // because TT's second parameter is a
                        // type parameter pack, while 17 does not name a type
eval<E<int, float>> eE; // error: E does not match TT in partial specialization
                        // because E's third (default) parameter is a non-type

Vor der Einführung von P0522R0 mussten die Template-Parameter von A exakt den entsprechenden Template-Parametern von P entsprechen. Dies verhinderte die Akzeptanz vieler sinnvoller Template-Argumente.

Obwohl es sehr früh darauf hingewiesen wurde (CWG#150), wurden die Änderungen zum Zeitpunkt seiner Behebung in das C++17-Arbeitspapier aufgenommen und die Auflösung wurde zu einem De-facto-C++17-Feature. Viele Compiler deaktivieren es standardmäßig.

  • GCC deaktiviert es in allen Sprachmodi vor C++17 standardmäßig, es kann in diesen Modi nur durch Setzen eines Compiler-Flags aktiviert werden.
  • Clang deaktiviert es in allen Sprachmodi standardmäßig, es kann nur durch Setzen eines Compiler-Flags aktiviert werden.
  • Microsoft Visual Studio behandelt es als normales C++17-Feature und aktiviert es nur in C++17 und späteren Sprachmodi (d. h. keine Unterstützung im C++14-Sprachmodus, der 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 after P0522R0
         // Error earlier: not an exact match
X<C> xc; // OK after P0522R0
         // Error earlier: not an exact match
 
template<template<class...> class Q> class Y { /* ... */ };
Y<A> ya; // OK
Y<B> yb; // OK
Y<C> yc; // OK
 
template<auto n> class D { /* ... */ };   // note: C++17
template<template<int> class R> class Z { /* ... */ };
Z<D> zd; // OK after P0522R0: the template parameter
         // is more specialized than the template argument
 
template<int> struct SI { /* ... */ };
template<template<auto> class> void FA(); // note: C++17
FA<SI>(); // Error

[bearbeiten] Standard-Template-Argumente

Standard-Template-Argumente werden in den Parameterlisten nach dem Zeichen = angegeben. Standardwerte können für jede Art von Template-Parameter (Typ, Nicht-Typ oder Template) angegeben werden, aber nicht für Parameterpacks(seit C++11).

Wenn der Standardwert für einen Template-Parameter einer primären Klassenvorlage, primären Variablentemplate,(seit C++14) oder Alias-Template angegeben wird, muss jeder nachfolgende Template-Parameter ein Standardargument haben, außer das allerletzte darf ein Template-Parameterpack sein(seit C++11). In einer Funktionsvorlage gibt es keine Einschränkungen für die Parameter, die auf einen Standardwert folgen, und ein Parameterpack darf von weiteren Typ-Parametern gefolgt werden, nur wenn diese Standardwerte haben oder aus den Funktionsargumenten abgeleitet werden können(seit C++11).

Standardparameter sind nicht erlaubt

  • in der Out-of-Class-Definition eines Mitglieds einer Klassenvorlage (sie müssen in der Deklaration innerhalb des Klassenrumpfes angegeben werden). Beachten Sie, dass Mitgliedsvorlagen von Nicht-Template-Klassen Standardparameter in ihren Out-of-Class-Definitionen verwenden können (siehe GCC-Bug 53856).
  • in Friend-Klassenvorlagen-Deklarationen
(bis C++11)

Bei einer Friend-Funktionsvorlagen-Deklaration sind Standard-Template-Argumente nur erlaubt, wenn die Deklaration eine Definition ist und keine anderen Deklarationen dieser Funktion in dieser Übersetzungseinheit vorkommen.

(seit C++11)

Standard-Template-Argumente, die in den Deklarationen erscheinen, werden ähnlich wie standardmäßige Funktionsargumente zusammengeführt.

template<typename T1, typename T2 = int> class A;
template<typename T1 = int, typename T2> class A;
 
// the above is the same as the following:
template<typename T1 = int, typename T2 = int> class A;

Aber derselbe Parameter kann nicht zweimal im selben Geltungsbereich Standardargumente erhalten.

template<typename T = int> class X;
template<typename T = int> class X {}; // error

Beim Parsen eines Standard-Template-Arguments für einen Nicht-Typ-Template-Parameter wird das erste nicht-verschachtelte > als Ende der Template-Parameterliste und nicht als Größer-als-Operator betrachtet.

template<int i = 3 > 4>   // syntax error
class X { /* ... */ };
 
template<int i = (3 > 4)> // OK
class Y { /* ... */ };

Die Template-Parameterlisten von Template-Template-Parametern können eigene Standardargumente haben, die nur dort wirksam sind, wo der Template-Template-Parameter selbst im Geltungsbereich liegt.

// class template, with a type template parameter with a default
template<typename T = float>
struct B {};
 
// template template parameter T has a parameter list, which
// consists of one type template parameter with a default
template<template<typename = float> typename T>
struct A
{
    void f();
    void g();
};
 
// out-of-body member function template definitions
 
template<template<typename TT> class T>
void A<T>::f()
{
    T<> t; // error: TT has no default in scope
}
 
template<template<typename TT = char> class T>
void A<T>::g()
{
    T<> t; // OK: t is T<char>
}

Mitgliederzugriff für die in einem Standard-Template-Argument verwendeten Namen wird bei der Deklaration geprüft, nicht am Verwendungsort.

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; // error: C::TT is protected

Das Standard-Template-Argument wird implizit instanziiert, wenn der Wert dieses Standardarguments benötigt wird, es sei denn, das Template wird verwendet, um eine Funktion zu benennen.

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>*
(seit C++14)

[bearbeiten] Template-Argument-Äquivalenz

Die Template-Argument-Äquivalenz wird verwendet, um festzustellen, ob zwei Template-Bezeichner gleich sind.

Zwei Werte sind template-argument-äquivalent, wenn sie vom selben Typ sind und eine der folgenden Bedingungen erfüllt ist:

  • Sie sind vom Integral- oder Aufzählungstyp und ihre Werte sind gleich.
  • Sie sind vom Zeigertyp und haben denselben Zeigerwert.
  • Sie sind vom Zeiger-auf-Mitglied-Typ und beziehen sich auf dasselbe Klassenmitglied oder sind beide der Null-Mitgliedszeigerwert.
  • Sie sind vom Lvalue-Referenztyp und beziehen sich auf dasselbe Objekt oder dieselbe Funktion.
(seit C++11)
  • Sie sind vom Gleitkommatyp und ihre Werte sind identisch.
  • Sie sind vom Arraytyp (in diesem Fall müssen die Arrays Mitgliedsobjekte einer Klasse/Union sein) und ihre entsprechenden Elemente sind template-argument-äquivalent.
  • Sie sind vom Union-Typ und entweder haben sie beide kein aktives Mitglied, oder sie haben dasselbe aktive Mitglied und ihre aktiven Mitglieder sind template-argument-äquivalent.
  • Sie sind von einem Lambda-Closure-Typ.
  • Sie sind von einem Nicht-Union-Klassentyp und ihre entsprechenden direkten Unterobjekte und Referenzmember sind template-argument-äquivalent.
(seit C++20)

[bearbeiten] Hinweise

In Template-Parametern können Typbeschränkungen sowohl für Typ- als auch für Nicht-Typ-Parameter verwendet werden, je nachdem, ob auto vorhanden ist.

template<typename>
concept C = true;
 
template<C,     // type parameter 
         C auto // non-type parameter
        >
struct S{};
 
S<int, 0> s;


(seit C++20)
Feature-Testmakro Wert Std Feature
__cpp_nontype_template_parameter_auto 201606L (C++17) Deklaration von Nicht-Typ-Template-Parametern mit auto
__cpp_template_template_args 201611L (C++17)
(DR)
Abgleich von Template-Template-Argumenten
__cpp_nontype_template_args 201411L (C++17) Erlaubt konstante Auswertung für alle Nicht-Typ-Template-Argumente
201911L (C++20) Klassentypen und Gleitkommatypen in Nicht-Typ-Template-Parametern

[bearbeiten] Beispiele

#include <array>
#include <iostream>
#include <numeric>
 
// simple non-type template parameter
template<int N>
struct S { int a[N]; };
 
template<const char*>
struct S2 {};
 
// complicated non-type example
template
<
    char c,             // integral type
    int (&ra)[5],       // lvalue reference to object (of array type)
    int (*pf)(int),     // pointer to function
    int (S<10>::*a)[10] // pointer to member object (of type int[10])
>
struct Complicated
{
    // calls the function selected at compile time
    // and stores the result in the array selected at compile time
    void foo(char base)
    {
        ra[4] = pf(c - base);
    }
};
 
//  S2<"fail"> s2;        // error: string literal cannot be used
    char okay[] = "okay"; // static object with linkage
//  S2<&okay[0]> s3;      // error: array element has no linkage
    S2<okay> s4;          // works
 
int a[5];
int f(int n) { return n; }
 
// C++20: NTTP can be a literal class type
template<std::array arr>
constexpr
auto sum() { return std::accumulate(arr.cbegin(), arr.cend(), 0); }
 
// C++20: class template arguments are deduced at the call site
static_assert(sum<std::array<double, 8>{3, 1, 4, 1, 5, 9, 2, 6}>() == 31.0);
// C++20: NTTP argument deduction and CTAD
static_assert(sum<std::array{2, 7, 1, 8, 2, 8}>() == 28);
 
int main()
{
    S<10> s; // s.a is an array of 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

[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 150
(P0522R0)
C++98 Template-Template-Argumente mussten mit Parameterlisten
von Template-Template-Parametern exakt übereinstimmen
spezialisierter
auch erlaubt
CWG 184 C++98 ob die Template-Parameter von Template-Template-Parametern
Standardargumente haben dürfen, ist unbestimmt
Spezifikation hinzugefügt
CWG 354 C++98 Nullzeigerwerte konnten keine Nicht-Typ-Template-Argumente sein erlaubt
CWG 1398 C++11 Nicht-Typ-Template-Argumente konnten keinen Typ std::nullptr_t haben erlaubt
CWG 1570 C++98 Nicht-Typ-Template-Argumente konnten Adressen von Unterobjekten bezeichnen nicht erlaubt
CWG 1922 C++98 es war unklar, ob eine Klassenvorlage, deren Name eine
injected-class-name ist, die Standardargumente in früheren Deklarationen verwenden kann
erlaubt
CWG 2032 C++14 für Variablentemplates gab es keine Einschränkung für die Template-Parameter
nach einem Template-Parameter mit einem Standard-Template-Argument
die gleiche Einschränkung anwenden
wie bei Klassenvorlagen
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 Capture-los
P2308R1 C++11
C++20
1. Listeninitialisierung war nicht erlaubt für
    Nicht-Typ-Template-Argumente (C++11)
2. es war unklar, wie Nicht-Typ-Template-Parameter
    von Klassentypen initialisiert werden (C++20)
1. erlaubt
2. geklärt