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:
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<auto n> 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:
T
, the value of P is A (as converted).T
, the value of P is A (as converted).T
is not a class type and A is an expression:T
, the value of P is A (as converted).T
is a class type or A is a braced-enclosed initializer list), a temporary variable constexpr T v = A; is introduced.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.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
The following limitations apply when instantiating templates that have constant template parameters:
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
typeid
;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[edit] Type template arguments
A template argument for a type template parameter must be a type-id, which may name an incomplete type:
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 }[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<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
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):
P
or A
.X
with template arguments corresponding to the template parameters from the respective function template where, for each template parameter PP
in the template parameter list of the function template, a corresponding template argument AA
is formed. If PP
declares a parameter pack, then AA
is the pack expansion PP...
; otherwise,(since C++11) AA
is the id-expression PP
.If the rewrite produces an invalid type, then P
is not at least as specialized as 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 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:
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[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:
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<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 }[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.
[edit] Example [edit] Defect reportsThe following behavior-changing defect reports were applied retroactively to previously published C++ standards.
DR Applied to Behavior as published Correct behavior CWG 150std::nullptr_t
allowed CWG 1570 C++98 constant template arguments could designate addresses of subobjects not allowed P2308R1 C++11
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4