A template is a pattern from which the compiler creates classes, functions, variables or aliases [temp]. Each set of arguments gives a separate specialisation. Since the compiler needs a template’s definition wherever it is instantiated, templates are usually defined in full in headers. C++20 concepts constrain templates.
Kinds of template
template-declaration:
template-head declaration
template-head concept-definition
template-head:
template < template-parameter-list > [requires-clause]
| Kind | Example | Since | Section |
|---|---|---|---|
| Class | template<class T> class Stack; | C++98 | [temp.class] |
| Function | template<class T> T max(T a, T b); | C++98 | [temp.fct] |
| Member | template<class U> void put(U);, in a class | C++98 | [temp.mem] |
| Alias | template<class T> using Vec = std::vector<T>; | C++11 | [temp.alias] |
| Variable | template<class T> constexpr T pi = T(3.14159); | C++14 | [temp.pre] |
| Concept | template<class T> concept C = ...; | C++20 | [temp.concept] |
template<class T, std::size_t N = 16>
class Stack {
public:
void push(const T& value);
template<class U> void push_all(const U& range); // a member template
private:
T items_[N];
std::size_t size_ = 0;
};
template<class T, std::size_t N>
void Stack<T, N>::push(const T& value)
{
items_[size_++] = value;
}
Stack<int> a; // Stack<int, 16>
Stack<double, 4> b;
Template parameters
| Kind | Form | Accepts |
|---|---|---|
| Type | class T or typename T | A type |
| Non-type | int N, auto V (C++17) | A constant of a structural type |
| Template | template<class> class C | A class template or alias template |
| Pack | class... Ts, int... Ns | Any number of the above |
A non-type parameter must have a structural type [temp.param]: an integer,
enumeration, pointer, pointer to member, std::nullptr_t or lvalue reference.
Since C++20 it can also be a floating-point type, or a literal class whose bases and non-static
members are all public, non-mutable and structural.
A parameter may have a default. Function template parameters may have defaults since C++11.
Instantiation
A template is instantiated when it is needed, and only the members actually used are instantiated [temp.inst].
| Form | Syntax | Effect |
|---|---|---|
| Implicit | Stack<int> a; | The compiler instantiates as needed |
| Explicit definition | template class Stack<int>; | Forces instantiation of all members in this translation unit [temp.explicit] |
| Explicit declaration (C++11) | extern template class Stack<int>; | Instantiation occurs elsewhere, saving compile time |
Specialisation
template<class T> struct Traits { static constexpr bool special = false; };
template<> struct Traits<int> { static constexpr bool special = true; }; // explicit
template<class T> struct Traits<T*> { static constexpr bool special = true; }; // partial
| Form | Header | Available for | Section |
|---|---|---|---|
| Explicit (full) | template<> | Class, function, variable, member | [temp.expl.spec] |
| Partial | template<...> with a restricted argument list | Class and variable templates only | [temp.spec.partial] |
A function template cannot be partially specialised. Overload it instead. An alias template cannot be specialised at all. The compiler picks the most specialised matching partial specialisation, or the primary template if none matches [temp.spec.partial.match].
Argument deduction
Function template arguments can be deduced from a call [temp.deduct], [temp.deduct.call].
template<class T> void f(T);
template<class T> void g(T&);
template<class T> void h(T&&);
int i = 0;
const int c = 0;
f(i); // T = int
f(c); // T = int: top-level const dropped
g(i); // T = int
g(c); // T = const int
h(i); // T = int&: forwarding reference with an lvalue
h(1); // T = int
Forwarding references
A parameter written T&& in a function template, where
T is deduced, is a forwarding reference
[temp.deduct.call]. An lvalue deduces T as an lvalue reference, and by
reference collapsing the
parameter becomes an lvalue reference. std::forward<T> then passes the
argument on with its original value category. auto&& works the same way.
template<class T, class... Args>
std::unique_ptr<T> make(Args&&... args)
{
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
Class template argument deduction
Since C++17 the arguments of a class template can be deduced from a constructor call. Implicit guides come from the constructors, and deduction guides can be written by hand [over.match.class.deduct], [temp.deduct.guide].
std::pair p{1, 2.5}; // std::pair<int, double>
std::vector v{1, 2, 3}; // std::vector<int>
template<class T> struct Box { Box(T); };
Box(const char*) -> Box<std::string>; // a deduction guide
Box b("text"); // Box<std::string>
Two-phase lookup
Names in a template are checked at two times [temp.res].
- Non-dependent names do not depend on a template parameter. They are looked up when the template is defined.
- Dependent names do. They are looked up when the template is instantiated, and argument-dependent lookup also runs at that point [temp.dep.candidate].
The compiler assumes a dependent name is a value unless told otherwise.
typename says it is a type, and template says it is a
template [temp.res], [temp.names]. Since C++20 typename is optional in
many contexts where only a type can appear.
template<class T>
void use(T& c)
{
typename T::value_type v = c.front(); // a dependent type name
auto x = c.template get<int>(); // a dependent member template
}
template<class T>
struct Derived : Base<T> {
void g() { this->f(); } // this-> because Base<T> is dependent
};
SFINAE
Substitution failure is not an error: when substituting deduced arguments into a function template’s signature produces an invalid type or expression in the immediate context, that candidate is removed from the overload set instead of causing a compile-time error [temp.deduct]. Errors elsewhere, such as in the function body, stay errors.
template<class T,
std::enable_if_t<std::is_integral_v<T>, int> = 0>
T twice(T v) { return v * 2; }
template<class T>
auto size_of(const T& c) -> decltype(c.size()) { return c.size(); } // needs .size()
template<class, class = void>
struct has_value_type : std::false_type { };
template<class T>
struct has_value_type<T, std::void_t<typename T::value_type>> : std::true_type { };
enable_if, expression SFINAE and void_tSince C++20, concepts replace most uses of
SFINAE with clearer syntax. if constexpr replaces many others inside
function bodies; see Statements.
Variadic templates
A parameter pack (C++11) accepts any number of arguments [temp.variadic].
sizeof...(pack) gives its length, and pack... expands it where a
list is allowed.
template<class... Ts>
void show(const Ts&... args)
{
std::size_t n = sizeof...(Ts);
(std::cout << ... << args); // a fold expression (C++17)
}
Fold expressions
A fold expression (C++17) applies a binary operator across a pack [expr.prim.fold].
| Form | Syntax | Expands to |
|---|---|---|
| Unary right fold | (pack op ...) | p1 op (... op (pN-1 op pN)) |
| Unary left fold | (... op pack) | ((p1 op p2) op ...) op pN |
| Binary right fold | (pack op ... op init) | p1 op (... op (pN op init)) |
| Binary left fold | (init op ... op pack) | (((init op p1) op p2) ...) op pN |
A unary fold over an empty pack is valid only for three operators: && gives
true, || gives false and , gives
void(). Any other operator makes it ill-formed.