Namespaces
Variants
Actions

Template arguments

From cppreference.com
< cpp‎ | language
 
 
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
 
 

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

[edit]Constant template arguments

Also known as non-type template arguments (see below).

The template argument that can be used with a constant template parameter can be any manifestly constant-evaluated expression.

(until C++11)

The template argument that can be used with a constant template parameter can be any initializer clause. If the initializer clause is an expression, it must be manifestly constant-evaluated.

(since C++11)

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 sitetemplate<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:

(until C++11)
  • If A is an expression:
  • 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:
  • Otherwise (T is a class type or A is a braced-enclosed initializer list), a temporary variable constexpr T v = A; is introduced.
  • 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:
  • Otherwise, the value of P is that of v.
(since 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

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<constint* 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, constchar* 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;staticint 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<constint& CRI>struct B {};   B<1> b2;// error: temporary would be required for template argumentint 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 typetypedefstruct{} 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 templateclass A {int x;};   template<typename T>// partial specializationclass A<T*>{long x;};   // class template with a template template parameter Vtemplate<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):

  • Each of the two function templates has the same template parameters, respectively, as P or A.
  • Each function template has a single function parameter whose type is a specialization of 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:

  • 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 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++17template<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:

  • 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.
(since C++11)
  • 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<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.

Feature-test macro ValueStdFeature
__cpp_template_template_args201611L(C++17)
(DR)
Matching of template template arguments
__cpp_nontype_template_args201411L(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 parameter
lists of template-template parameters exactly
more specialized
also allowed
CWG 354C++98 null pointer values could not be constant template arguments allowed
CWG 1398C++11 constant template arguments could not have type std::nullptr_tallowed
CWG 1570C++98 constant template arguments could designate addresses of subobjects not allowed
P2308R1C++11
C++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. allowed
2. made clear
close