Template arguments - cppreference.com (original) (raw)
In order for a template to be instantiated, every template parameter must be replaced by a corresponding template argument. The arguments are either explicitly provided, deduced or defaulted.
Each parameter in template-parameter-list (see template identifier syntax) belongs to one of the following categories:
- constant template argument
- type template argument
- template template argument
Contents
- 1 Constant template arguments
- 2 Type template arguments
- 3 Template template arguments
- 4 Template argument equivalence
- 5 Ambiguity resolution
- 6 Notes
- 7 Example
- 8 Defect reports
[edit] Constant template arguments
Also known as non-type template arguments (see below).
Given the type of the constant template parameter declaration as T and the template argument provided for the parameter as E.
| The invented declaration T x = E; must satisfy the semantic constraints for the definition of a constexpr variable with static storage duration. | (since C++20) |
|---|---|
| If T contains a placeholder type, or is a placeholder for a deduced class type, the type of the template parameter is the type deduced for the variable x in the invented declaration T x = E;.If a deduced parameter type is not a structural type, the program is ill-formed.For constant template parameter packs whose type uses a placeholder type, the type is independently deduced for each template argument and need not match. | (since C++17) |
template struct B { /* ... */ }; B<5> b1; // OK: constant template parameter type is int B<'a'> b2; // OK: constant template parameter type is char B<2.5> b3; // error (until C++20): constant 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
The value of a constant template parameter P of (possibly deduced)(since C++17) type T is determined from its template argument A as follows:
| If A is a converted constant expression of type T, the value of P is A (as converted). Otherwise, the program is ill-formed. | (until C++11) |
|---|---|
| If A is an expression: If A is a converted constant expression of type T, the value of P is A (as converted). Otherwise, the program is ill-formed. Otherwise (A is a braced-enclosed initializer list), a temporary variable constexpr T v = A; is introduced. The value of P is that of v. The lifetime of v ends immediately after initializing it. | (since C++11)(until C++20) |
| If T is not a class type and A is an expression: If A is a converted constant expression of type T, the value of P is A (as converted). Otherwise, the program is ill-formed. Otherwise (T is a class type or A is a braced-enclosed initializer list), a temporary variable constexpr T v = A; is introduced. If T is a class type, a template parameter object exists (which is also denoted by P). P is copy-initialized from an unspecified candidate initializer that is template-argument-equivalent to v. The lifetime of v ends immediately after initializing it and P. If the initialization of P satisfies any of the following conditions, the program is ill-formed: The initialization would be ill-formed. The full-expression of an invented declarator-initializer sequence for the initialization would not be a constant expression when interpreted as a manifestly constant-evaluated expression. The initialization would cause P to not be template-argument-equivalent to v. Otherwise, the value of P is that of v. | (since C++20) |
template struct C { /* ... / }; C<{42}> c1; // OK template 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
| The following limitations apply when instantiating templates that have constant template parameters: For integral and arithmetic types, the template argument provided during instantiation must be a converted constant expression of the template parameter's type (so certain implicit conversion applies). For pointers to objects, the template arguments have to designate the address of a complete object with static storage duration and a linkage (either internal or external), or a constant expression that evaluates to the appropriate null pointer or std::nullptr_t(since C++11) value. For pointers to functions, the valid arguments are pointers to functions with linkage (or constant expressions that evaluate to null pointer values). For lvalue reference parameters, the argument provided at instantiation cannot be a temporary, an unnamed lvalue, or a named lvalue with no linkage (in other words, the argument must have linkage). For pointers to members, the argument has to be a pointer to member expressed as &Class::Member or a constant expression that evaluates to null pointer or std::nullptr_t(since C++11) value. In particular, this implies that string literals, addresses of array elements, and addresses of non-static members cannot be used as template arguments to instantiate templates whose corresponding constant template parameters are pointers to objects. | (until C++17) |
|---|---|
| constant template parameters of reference or pointer type and non-static data members of reference or pointer type in a constant template parameter of class type and its subobjects(since C++20) cannot refer to/be the address of a temporary object (including one created during reference initialization); a string literal; the result of typeid; the predefined variable __func__; or a subobject (including non-static class member, base subobject, or array element) of one of the above(since C++20). | (since C++17) |
template<const int* pci> struct X {}; int ai[10]; X xi; // OK: array to pointer conversion and cv-qualification conversion struct Y {}; template<const Y& b> struct Z {}; Y y; Z z; // OK: no conversion template<int (&pa)[5]> struct W {}; int b[5]; W 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 b1; // OK
[edit] Type template arguments
A template argument for a type template parameter must be a type-id, which may name an incomplete type:
template class X {}; // class template struct A; // incomplete type typedef struct {} B; // type alias to an unnamed type int main() { X x1; // OK: 'A' names a type X<A*> x2; // OK: 'A*' names a type X x3; // OK: 'B' names a type }
[edit] Template template arguments
A template argument for a template template parameter must be an id-expression which names a class template or a template alias.
When the argument is a class template, only the primary template is considered when matching the parameter. The partial specializations, if any, are only considered when a specialization based on this template template parameter happens to be instantiated.
template // primary template class A { int x; }; template // partial specialization class A<T*> { long x; }; // class template with a template template parameter V template<template class V> class C { V y; // uses the primary template V<int*> z; // uses the partial specialization }; C c; // c.y.x has type int, c.z.x has type long
To match a template template argument A to a template template parameter P, P must be at least as specialized as A (see below). If P's parameter list includes a parameter pack, zero or more template parameters (or parameter packs) from A's template parameter list are matched by it.(since C++11)
Formally, a template template-parameter P is at least as specialized as a template template argument A if, given the following rewrite to two function templates, the function template corresponding to P is at least as specialized as the function template corresponding to A according to the partial ordering rules for function templates. Given an invented class template X with the template parameter list of A (including default arguments):
- Each of the two function templates has the same template parameters, respectively, as
PorA. - Each function template has a single function parameter whose type is a specialization of
Xwith template arguments corresponding to the template parameters from the respective function template where, for each template parameterPPin the template parameter list of the function template, a corresponding template argumentAAis formed. IfPPdeclares a parameter pack, thenAAis the pack expansionPP...; otherwise,(since C++11)AAis the id-expressionPP.
If the rewrite produces an invalid type, then P is not at least as specialized as A.
template struct eval; // primary template template<template<typename, typename...> class TT, typename T1, typename... Rest> struct eval<TT<T1, Rest...>> {}; // partial specialization of eval template struct A; template<typename T1, typename T2> struct B; template struct C; template<typename T1, int N> struct D; template<typename T1, typename T2, int N = 17> struct E; eval<A> 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 constant
Before the adoption of P0522R0, each of the template parameters of A must match corresponding template parameters of P exactly. This hinders many reasonable template argument from being accepted.
Although it was pointed out very early (CWG#150), by the time it was resolved, the changes were applied to the C++17 working paper and the resolution became a de facto C++17 feature. Many compilers disable it by default:
- GCC disables it in all language modes prior to C++17 by default, it can only be enabled by setting a compiler flag in these modes.
- Clang disables it in all language modes by default, it can only be enabled by setting a compiler flag.
- Microsoft Visual Studio treats it as a normal C++17 feature and only enables it in C++17 and later language modes (i.e. no support in C++14 language mode, which is the default mode).
template class A { /* ... / }; template<class T, class U = T> class B { / ... / }; template<class... Types> class C { / ... / }; template<template class P> class X { / ... / }; X xa; // OK X xb; // OK after P0522R0 // Error earlier: not an exact match X xc; // OK after P0522R0 // Error earlier: not an exact match template<template<class...> class Q> class Y { / ... / }; Y ya; // OK Y yb; // OK Y yc; // OK template class D { / ... / }; // note: C++17 template<template class R> class Z { / ... / }; Z zd; // OK after P0522R0: the template parameter // is more specialized than the template argument template struct SI { / ... */ }; template<template class> void FA(); // note: C++17 FA(); // Error
[edit] Template argument equivalence
Template argument equivalence is used to determine whether two template identifiers are same.
Two values are template-argument-equivalent if they are of the same type and any of the following conditions is satisfied:
- They are of integral or enumeration type and their values are the same.
- They are of pointer type and they have the same pointer value.
- They are of pointer-to-member type and they refer to the same class member or are both the null member pointer value.
- They are of lvalue reference type and they refer to the same object or function.
| They are of floating-point type and their values are identical. They are of array type (in which case the arrays must be member objects of some class/union) and their corresponding elements are template-argument-equivalent. They are of union type and either they both have no active member or they have the same active member and their active members are template-argument-equivalent. They are of a lambda closure type. They are of non-union class type and their corresponding direct subobjects and reference members are template-argument-equivalent. | (since C++20) |
|---|
[edit] Ambiguity resolution
If a template argument can be interpreted as both a type-id and an expression, it is always interpreted as a type-id, even if the corresponding template parameter is constant:
template void f(); // #1 template void f(); // #2 void g() { f<int()>(); // “int()” is both a type and an expression, // calls #1 because it is interpreted as a type }
[edit] Notes
Before C++26, constant template argument were called non-type template argument in the standard wording. The terminology was changed by P2841R6 / PR #7587.
| Feature-test macro | Value | Std | Feature |
|---|---|---|---|
| __cpp_template_template_args | 201611L | (C++17)(DR) | Matching of template template arguments |
| __cpp_nontype_template_args | 201411L | (C++17) | Allow constant evaluation for all constant template arguments |
| 201911L | (C++20) | Class types and floating-point types in constant template parameters |
[edit] Example
[edit] Defect reports
The following behavior-changing defect reports were applied retroactively to previously published C++ standards.
| DR | Applied to | Behavior as published | Correct behavior |
|---|---|---|---|
| CWG 150(P0522R0) | C++98 | template-template arguments had to match parameterlists of template-template parameters exactly | more specializedalso allowed |
| CWG 354 | C++98 | null pointer values could not be constant template arguments | allowed |
| CWG 1398 | C++11 | constant template arguments could not have type std::nullptr_t | allowed |
| CWG 1570 | C++98 | constant template arguments could designate addresses of subobjects | not allowed |
| P2308R1 | C++11C++20 | 1. list-initialization was not allowed for constant template arguments (C++11)2. it was unclear how constant template parameters of class types are initialized (C++20) | 1. allowed2. made clear |