Templates and Generic Programming
Generic Programming: Writing code that works independently of a specific type.
Template: A blueprint used by the compiler to generate functions or classes for specific types or values.
Generic programming differs from object-oriented programming in when the relevant types become known.
OOP
types may vary at run time
Generic Programming
types are determined during compilation
Templates are the foundation of generic programming in C++.
16.1 Defining a Template
A template lets one function or class definition work with many types.
Without templates, similar operations often require repeated overloads.
int compare(
const std::string& lhs,
const std::string& rhs);
int compare(
const double& lhs,
const double& rhs);
The bodies would be almost identical.
A template removes this duplication.
16.1.1 Function Templates
Function Template: A formula from which the compiler generates specific function instances.
Basic syntax:
template <typename T>
int compare(
const T& lhs,
const T& rhs)
{
if (lhs < rhs)
{
return -1;
}
if (rhs < lhs)
{
return 1;
}
return 0;
}
T is a template type parameter.
Using a Function Template
compare(10, 20);
The compiler deduces:
T = int
and generates a function equivalent in shape to:
int compare(
const int& lhs,
const int& rhs);
Another call:
compare(
std::string("a"),
std::string("b")
);
deduces:
T = std::string
Template Instantiation
Instantiation: The process by which the compiler generates a concrete function or class from a template.
Conceptually:
template definition
+
template arguments
↓
specific instance
Each distinct set of template arguments may produce a different instantiation.
typename and class
For a type parameter, these are equivalent:
template <typename T>
and:
template <class T>
Both declare T as a type parameter.
typename often makes the intent clearer.
Nontype Template Parameters
A template parameter can represent a compile-time constant value instead of a type.
Example:
template <
unsigned N,
unsigned M
>
int compare(
const char (&lhs)[N],
const char (&rhs)[M])
{
return std::strcmp(
lhs,
rhs
);
}
Here:
N
M
```
are nontype template parameters.
The compiler can deduce the sizes from string literals or arrays.
---
### Requirements on Nontype Arguments
A nontype template argument must be a compile-time constant of an allowed kind.
Its value becomes part of the generated template instance.
For example:
~~~cpp
template <
typename T,
std::size_t N
>
void print(
const T (&array)[N])
{
for (const auto& value
: array)
{
std::cout
<< value
<< ' ';
}
}
Usage:
int values[3] = {
1,
2,
3
};
print(values);
The compiler deduces:
T = int
N = 3
Template Compilation Errors
A template is checked in two broad stages.
The compiler checks the template definition for syntax that does not depend on template arguments.
Later, when a specific template is instantiated, operations involving the template arguments must be valid.
For example:
template <typename T>
T add(
const T& lhs,
const T& rhs)
{
return lhs + rhs;
}
works only for types that support the required + operation.
Templates Are Usually Defined in Headers
The compiler generally needs to see the complete template definition when it instantiates the template.
Therefore template declarations and definitions are usually placed in header files.
Typical structure:
compare.hpp
template declaration
template definition
Unlike ordinary functions, separating a template definition into an unrelated source file usually prevents other translation units from instantiating it unless explicit-instantiation techniques are used.
inline and constexpr Templates
A function template may also be declared inline or constexpr.
template <typename T>
inline T minimum(
const T& lhs,
const T& rhs)
{
return
rhs < lhs
? rhs
: lhs;
}
The keywords appear after the template parameter list.
16.1.2 Class Templates
Class Template: A blueprint used to generate classes for different template arguments.
Example:
template <typename T>
class Box
{
public:
Box() = default;
explicit Box(
const T& value)
: data(value)
{
}
T& get()
{
return data;
}
const T& get() const
{
return data;
}
private:
T data{};
};
Instantiating a Class Template
Unlike function templates, class-template arguments usually must be specified explicitly in C++11.
Box<int> integer_box(
42
);
Box<std::string> text_box(
"hello"
);
These are distinct class types.
Box<int>
Box<string>
```
---
### Each Instantiation Is a Distinct Type
~~~cpp
Box<int> a;
Box<double> b;
Box<int> and Box<double> are unrelated class types produced from the same template definition.
They cannot be assigned merely because they come from the same class template.
Using Template Parameters inside the Class
Inside the class template:
template <typename T>
class Box
{
private:
T data;
};
T is used exactly as a type name.
It may appear in:
- data members
- parameters
- return types
- type aliases
- nested template expressions
Referring to the Current Instantiation
Inside the class-template body, the template name can usually stand for the current instantiation.
template <typename T>
class Box
{
public:
Box(
const Box& other)
: data(
other.data)
{
}
private:
T data;
};
Here:
Box
means:
Box<T>
within the class body.
Defining Members outside a Class Template
Outside the class, both the template parameter list and the template arguments are required.
template <typename T>
T&
Box<T>::get()
{
return data;
}
The pattern is:
template declaration
return type
ClassName<template-arguments>::member
Members Are Instantiated Only When Used
Instantiating a class template does not necessarily instantiate every member function immediately.
A member function is generally instantiated when it is used.
This allows a class template to contain members that require operations not supported by every possible element type, as long as those members are not used for incompatible instantiations.
Class Template and Friends
Friendship involving templates can take several forms.
A class may make:
- one specific template instantiation a friend
- all instantiations of a template a friend
- a non-template function a friend
The friend declaration determines which instantiations receive access.
One-to-One Friendship
A class-template instantiation can befriend the corresponding instantiation of another template.
Conceptually:
Blob<T>
friends with
BlobPtr<T>
This preserves a one-to-one relationship between template arguments.
Generic Friendship
A template can grant friendship to all instantiations of another template.
The friend template parameter is independent of the enclosing template's parameter.
This is useful when every specialization needs access.
Template Type Aliases
A template alias can create a shorter template name.
template <typename T>
using Twin =
std::pair<T, T>;
Usage:
Twin<std::string> names;
means:
std::pair<
std::string,
std::string
>
Fixed Template Arguments in an Alias
template <typename T>
using StringMap =
std::map<
std::string,
T
>;
Usage:
StringMap<int> counts;
means:
std::map<
std::string,
int
>
Static Members of Class Templates
Each class-template instantiation has its own static members.
template <typename T>
class Counter
{
public:
static std::size_t count;
};
Then:
Counter<int>::count
Counter<double>::count
```
are distinct static objects.
---
### 16.1.3 Template Parameters
Template parameters obey normal scoping rules.
~~~cpp
template <typename T>
T identity(
const T& value)
{
T copy = value;
return copy;
}
T is visible from its declaration through the end of the template declaration or definition.
Template Parameter Names Have No Special Meaning
These are equivalent:
template <typename T>
template <typename Value>
template <typename Element>
The parameter name itself has no intrinsic semantic meaning.
Template Parameters Cannot Be Redeclared
This is invalid:
template <typename T>
void function(T value)
{
// Error:
// int T = 0;
}
A template parameter name cannot be reused inside its scope.
Template Declarations
A template declaration must include its template parameter list.
template <typename T>
int compare(
const T&,
const T&);
Class-template declaration:
template <typename T>
class Box;
Parameter Names May Differ between Declarations
These declarations can refer to the same template.
template <typename T>
void function(T);
template <typename Value>
void function(Value);
The position and meaning of the template parameters matter, not their spelling.
Dependent Type Names and typename
A name that depends on a template parameter may represent either:
- a type
- a static member or other value
The compiler cannot always know which.
Use typename when a dependent qualified name denotes a type.
template <typename T>
typename T::value_type
top(
const T& container)
{
return
container.back();
}
Without typename, the compiler does not assume that:
T::value_type
is a type.
Default Template Arguments
Template parameters can have default arguments.
template <
typename T,
typename Compare =
std::less<T>
>
class OrderedBox
{
};
Usage:
OrderedBox<int> box;
uses:
Compare = std::less<int>
An explicit comparator type can also be supplied.
Defaults for Function Templates
C++11 permits default template arguments for function templates as well.
However, function-template arguments are often deduced, so explicit defaults are needed less frequently than in class templates.
16.1.4 Member Templates
Member Template: A member function that is itself a template.
A member template may belong to either:
- an ordinary class
- a class template
Member Template of an Ordinary Class
class DebugDelete
{
public:
template <typename T>
void operator()(
T* pointer) const
{
delete pointer;
}
};
Usage:
double* value =
new double;
DebugDelete deleter;
deleter(value);
The function-call operator is instantiated for double.
Member Template of a Class Template
A class template can also contain a member template.
template <typename T>
class Box
{
public:
template <typename U>
Box(
const Box<U>& other);
private:
T data{};
};
Here:
T
class-template parameter
U
member-template parameter
They are separate template parameter lists.
Defining a Member Template outside the Class
Both template parameter lists must appear.
template <typename T>
template <typename U>
Box<T>::Box(
const Box<U>& other)
: data(
other.get())
{
}
The outer list belongs to the class template.
The inner list belongs to the member template.
Member Templates and Virtual Functions
A member template cannot be virtual.
Virtual dispatch requires a fixed virtual-function interface, whereas a member template represents a family of possible functions.
16.1.5 Controlling Instantiations
Templates are usually instantiated automatically when used.
Large programs can accidentally instantiate the same template in many translation units.
C++11 provides explicit instantiation declarations and definitions to control this behavior.
Explicit Instantiation Declaration
extern template
class std::vector<std::string>;
An explicit-instantiation declaration tells the compiler that the corresponding definition will be provided elsewhere.
Explicit Instantiation Definition
template
class std::vector<std::string>;
This forces instantiation of the specified template in that translation unit.
Typical Use
Header:
extern template
class Blob<std::string>;
One source file:
template
class Blob<std::string>;
Other translation units use the externally instantiated version rather than producing another full instantiation.
Definition Must Be Available
An explicit-instantiation definition requires the complete template definition to be visible.
Explicit instantiation changes where code is generated; it does not eliminate the need for the definition at the instantiation point.
16.1.6 Efficiency and Flexibility
Templates provide both:
- compile-time type flexibility
- opportunities for efficient type-specific code
The generated operations are specialized for the concrete template arguments.
Smart Pointer Deleters as an Example
shared_ptr and unique_ptr both support custom deleters, but their designs differ.
For unique_ptr, the deleter type is part of the pointer type.
std::unique_ptr<
int,
Deleter
>
For shared_ptr, different deleter types can be used without changing the visible shared_ptr<T> type.
This demonstrates that generic interfaces can make different tradeoffs between:
- run-time flexibility
- compile-time type information
- object size
- generated code
16.2 Template Argument Deduction
Template Argument Deduction: The process by which the compiler determines template arguments from function-call arguments.
Example:
template <typename T>
void print(
const T& value);
print(42);
The compiler deduces:
T = int
16.2.1 Conversions and Template Type Parameters
Template deduction permits fewer conversions than ordinary function calls.
For parameters that are not references, some normal adjustments are applied.
Top-Level const Is Ignored for Value Parameters
template <typename T>
void value_function(T);
If:
const int value = 42;
value_function(value);
then:
T = int
The top-level const is ignored because the parameter receives a copy.
Array-to-Pointer Conversion for Value Parameters
template <typename T>
void function(T);
Calling with:
const char text[] =
"hello";
function(text);
can deduce a pointer type because the array-to-pointer conversion is applied for a nonreference parameter.
Function-to-Pointer Conversion
Likewise, function arguments can be converted to function pointers when the template parameter is a value parameter.
Reference Parameters Preserve More Type Information
template <typename T>
void function(
const T&);
When a reference parameter is used, array arguments do not undergo ordinary array-to-pointer decay during deduction.
This makes it possible to deduce array bounds.
template <
typename T,
std::size_t N
>
std::size_t size(
const T (&)[N])
{
return N;
}
Same Template Parameter Must Deduce Consistently
template <typename T>
int compare(
const T& lhs,
const T& rhs);
This call is straightforward:
compare(10, 20);
Both arguments imply:
T = int
But a call such as:
// compare(10, 20.0);
cannot deduce one T that exactly matches both arguments.
Use explicit conversions or a template with separate type parameters when mixed types are intended.
16.2.2 Function-Template Explicit Arguments
Sometimes a template argument cannot be deduced from function parameters.
It must then be supplied explicitly.
Explicit Template Arguments
template <
typename T1,
typename T2,
typename T3
>
T1 sum(
T2 lhs,
T3 rhs)
{
return
lhs + rhs;
}
T1 appears only in the return type, so it cannot be deduced from the arguments.
Call:
auto result =
sum<long long>(
10,
20
);
The compiler explicitly receives:
T1 = long long
```
and deduces the remaining template arguments.
---
### Explicit Arguments Are Matched from the Left
Template arguments written explicitly correspond to template parameters from left to right.
Therefore template parameter order affects which arguments can conveniently be left for deduction.
---
### Ordinary Conversions after Explicit Arguments
Once a template argument is explicitly fixed, normal conversions can sometimes be used to match the corresponding function parameter.
This differs from pure deduction, where conversions are deliberately limited.
---
### 16.2.3 Trailing Return Types and Type Transformation
Sometimes a function template's return type depends on an operation involving its arguments.
Example goal:
~~~text
return the element referred to by an iterator
The exact type may not be known until the parameter types are known.
A trailing return type solves this problem.
Trailing Return Type with decltype
template <typename It>
auto first_value(
It begin,
It end)
-> decltype(*begin)
{
return *begin;
}
The return type can use the function parameters because it appears after the parameter list.
decltype(*iterator) May Be a Reference
For many iterators:
*begin
is an lvalue.
Therefore:
decltype(*begin)
is often a reference type.
That is useful when the function should return the actual element.
Removing a Reference
If the function should return a value rather than a reference, use a type transformation.
Required header:
#include <type_traits>
Example:
template <typename It>
auto first_copy(
It begin,
It end)
->
typename std::remove_reference<
decltype(*begin)
>::type
{
return *begin;
}
remove_reference removes reference qualification from a type.
Type Transformation Templates
The standard library defines type traits such as:
remove_reference
add_const
remove_const
remove_pointer
add_pointer
```
These templates compute new types during compilation.
Many type traits expose the resulting type through a nested member named:
~~~cpp
type
Because that name depends on template arguments, typename is required.
16.2.4 Function Pointers and Argument Deduction
A function template can be instantiated to match a function-pointer target type.
Example:
template <typename T>
int compare(
const T& lhs,
const T& rhs);
Function pointer:
int (*pointer)(
const int&,
const int&) =
compare;
The target type determines:
T = int
Overloaded Context May Be Ambiguous
If the function template could generate more than one function compatible with an overloaded target context, explicit template arguments may be needed.
Function-pointer context participates in template argument deduction just as a function call can.
16.2.5 Template Argument Deduction and References
Reference parameters preserve important information about whether an argument is:
- const
- nonconst
- lvalue
- rvalue
Lvalue Reference Parameter
template <typename T>
void function(T&);
If called with:
int value = 42;
function(value);
then:
T = int
If called with:
const int value = 42;
function(value);
then:
T = const int
Because the parameter is a reference, low-level const information is preserved.
const T& Parameter
template <typename T>
void function(
const T&);
Both const and nonconst arguments can bind.
For a const-reference parameter, the constness added by the parameter itself is not part of the deduced T.
Rvalue Reference Parameter
template <typename T>
void function(T&&);
When T&& uses a deduced template parameter in this form, special rules apply.
It can bind to both:
- rvalues
- lvalues
This behavior is essential to forwarding.
Reference Collapsing
When references are combined through deduction or type aliases, C++ applies reference-collapsing rules.
The important results are:
T& & -> T&
T& && -> T&
T&& & -> T&
T&& && -> T&&
In short:
if either reference is an lvalue reference,
the result is an lvalue reference
Only:
rvalue reference + rvalue reference
```
remains an rvalue reference.
---
### Special Deduction Rule for `T&&`
For:
~~~cpp
template <typename T>
void function(T&&);
called with an lvalue:
int value = 42;
function(value);
the compiler deduces:
T = int&
Then:
T&&
= int& &&
= int&
```
after reference collapsing.
This is why such a parameter can bind to an lvalue.
---
### 16.2.6 Understanding `std::move`
`std::move()` is a function template that converts its argument into an rvalue expression suitable for move operations.
It does not itself move any data.
---
### Conceptual `std::move`
A simplified C++11-style implementation is:
~~~cpp
template <typename T>
typename std::remove_reference<T>::type&&
move(T&& value)
{
using ReturnType =
typename std::remove_reference<
T
>::type&&;
return
static_cast<ReturnType>(
value
);
}
The real library implementation is in namespace std.
Why remove_reference Is Needed
If an lvalue is passed:
std::string text;
std::move(text);
deduction gives conceptually:
T = string&
Without remove_reference, applying && could collapse back to an lvalue reference.
Removing the reference first ensures the return type is:
std::string&&
What std::move Actually Does
Conceptually:
std::move(expression)
↓
cast expression to rvalue
↓
move-enabled overload may be selected
It does not guarantee that a move occurs.
If the type has no usable move operation, a copy may still happen.
16.2.7 Forwarding
Forwarding: Passing a function argument onward while preserving its original type and value category.
A forwarding function may need to preserve:
- constness
- lvalue/rvalue category
The Forwarding Problem
Consider:
void process(
int& value);
void process(
const int& value);
A wrapper:
template <typename T>
void wrapper(T&& value)
{
process(value);
}
has a problem.
Even when value was initialized from an rvalue, the expression:
value
is an lvalue because it has a name.
std::forward
Use:
std::forward<T>(value)
to preserve the value category deduced for T.
template <typename T>
void wrapper(T&& value)
{
process(
std::forward<T>(
value
)
);
}
Required header:
#include <utility>
Forwarding Behavior
Conceptually:
original lvalue
-> forwarded as lvalue
original rvalue
-> forwarded as rvalue
Unlike std::move, std::forward performs an rvalue cast only when deduction indicates that the original argument was an rvalue.
16.3 Overloading and Templates
Function templates can participate in overload sets with:
- other templates
- ordinary non-template functions
The compiler uses ordinary overload resolution plus template-specific ordering rules.
Template and Non-Template Overloads
template <typename T>
std::string debug_rep(
const T& value);
std::string debug_rep(
const std::string& value);
A call with a std::string can match both.
When otherwise equally good, the non-template function is preferred.
More Specialized Templates
Suppose:
template <typename T>
std::string debug_rep(
const T& value);
template <typename T>
std::string debug_rep(
T* pointer);
For a pointer argument, the pointer-specific template is more specialized and is preferred.
Exact Match vs. Conversion
A template that provides an exact type match may be preferred over a non-template overload that requires a conversion.
Therefore overload resolution considers both:
- match quality
- template specialization ordering
The non-template is preferred only when competing functions are otherwise equally good matches.
Overload Set Must Be Declared before Template Definition
Names used inside a template are affected by what declarations are visible when the template is defined.
For overloaded helper functions, declare the complete intended overload set before defining templates that call them.
Otherwise the compiler may instantiate a less specific template instead of calling a later overload.
debug_rep Example
Typical overload set:
template <typename T>
std::string debug_rep(
const T& value);
template <typename T>
std::string debug_rep(
T* pointer);
std::string debug_rep(
const std::string& value);
std::string debug_rep(
char* pointer);
std::string debug_rep(
const char* pointer);
Different argument types select different overloads.
This example combines:
- overload resolution
- template deduction
- pointer specialization
- ordinary non-template overloads
16.4 Variadic Templates
Variadic Template: A template that accepts a varying number of template or function arguments.
A varying sequence is called a parameter pack.
Parameter Packs
There are two important pack kinds.
Template Parameter Pack: Represents zero or more template parameters.
typename... Args
Function Parameter Pack: Represents zero or more function parameters.
const Args&... rest
Example:
template <
typename T,
typename... Args
>
void function(
const T& first,
const Args&... rest);
sizeof...
The number of elements in a parameter pack can be inspected with:
sizeof...(Args)
or:
sizeof...(rest)
Example:
template <
typename... Args
>
void count_args(
const Args&... args)
{
std::cout
<< sizeof...(Args)
<< ' '
<< sizeof...(args);
}
16.4.1 Writing a Variadic Function Template
A classic variadic example prints an arbitrary number of arguments.
Base case:
template <typename T>
std::ostream&
print(
std::ostream& output,
const T& value)
{
return
output << value;
}
Variadic case:
template <
typename T,
typename... Args
>
std::ostream&
print(
std::ostream& output,
const T& value,
const Args&... rest)
{
output
<< value
<< ", ";
return
print(
output,
rest...
);
}
Recursive Pack Processing
Call:
print(
std::cout,
10,
"hello",
3.14
);
Conceptually:
print(10, "hello", 3.14)
↓
print("hello", 3.14)
↓
print(3.14)
↓
base case
The pack becomes smaller at each recursive call.
Empty Packs Are Allowed
A parameter pack may contain zero parameters.
That fact allows a variadic template to eventually reach a nonvariadic base-case overload.
The base-case declaration must be visible when the variadic template is defined.
16.4.2 Pack Expansion
Pack Expansion: Replaces a parameter pack with its individual elements according to a pattern.
An expansion is written using:
...
after the pattern.
Expanding a Type Pack
const Args&... rest
applies the pattern:
const Args&
to every type in the pack.
If:
Args = <string, int>
```
the expanded parameters are conceptually:
~~~cpp
const std::string&,
const int&
Expanding a Function Parameter Pack
print(
output,
rest...
);
expands each argument stored in rest.
If:
rest = {text, number}
```
the call becomes conceptually:
~~~cpp
print(
output,
text,
number
);
Expansion Pattern
A pack expansion applies a complete pattern to each pack element.
Example:
debug_rep(rest)...
applies:
debug_rep(...)
to every argument in rest.
This differs from expanding only the name.
16.4.3 Forwarding Parameter Packs
Variadic templates can combine parameter packs with perfect forwarding.
Typical form:
template <
typename... Args
>
void emplace(
Args&&... args)
{
construct(
std::forward<Args>(
args
)...
);
}
Each argument retains its original value category.
Forwarding Expansion
The pattern:
std::forward<Args>(args)
is expanded for every pair of corresponding type and function parameters.
Conceptually, for two arguments:
std::forward<A1>(a1),
std::forward<A2>(a2)
This is the technique used by library functions such as emplacement operations to pass constructor arguments efficiently.
Why Args&&... Works
Each parameter is a forwarding reference using a deduced template parameter.
Reference-collapsing rules allow each argument to preserve whether it originated as:
- an lvalue
- an rvalue
std::forward then restores that category when passing it onward.
16.5 Template Specializations
Template Specialization: A custom definition used for a specific set of template arguments.
Specialization allows a generic template to have different behavior for a particular type.
Function-Template Specialization
Generic template:
template <typename T>
int compare(
const T& lhs,
const T& rhs)
{
if (lhs < rhs)
{
return -1;
}
if (rhs < lhs)
{
return 1;
}
return 0;
}
A specialization can define behavior for a specific type.
Syntax:
template <>
int compare(
const char* const& lhs,
const char* const& rhs)
{
return std::strcmp(
lhs,
rhs
);
}
The empty template parameter list:
template <>
indicates an explicit specialization.
Specialization Is Not Overloading
A specialization does not introduce a new independent overloaded template.
It provides a specialized definition of an existing template for specific template arguments.
This distinction affects:
- declarations
- lookup
- organization of source code
Original Template Must Be Declared First
The primary template must be declared before its specialization.
primary template declaration
↓
specialization
The compiler must know which template is being specialized.
Specialization Must Be in the Same Namespace
An explicit specialization is defined in the namespace of the original template.
For standard-library templates, user-defined specializations are permitted only in cases allowed by the language and library rules.
Class-Template Specialization
A class template can also be specialized.
Primary template:
template <typename T>
class Formatter
{
public:
std::string format(
const T& value) const;
};
Explicit specialization:
template <>
class Formatter<bool>
{
public:
std::string format(
bool value) const
{
return
value
? "true"
: "false";
}
};
Formatter<bool> now uses the specialized class definition.
Specialized Members Are Separate
A class-template specialization is a distinct class definition.
Its members do not have to match the exact implementation structure of the primary template.
The specialization must still provide whatever interface client code expects to use.
Use Specialization Sparingly
Before writing a specialization, consider whether ordinary overloading can express the desired behavior more clearly.
For function templates especially, ordinary overloads are often easier to understand and participate more naturally in overload resolution.
Specialization is most appropriate when one specific template argument genuinely requires a different implementation.
Essential Study Checklist
- Templates are the foundation of generic programming in C++.
- A function template generates functions for deduced or explicit template arguments.
- A class template generates distinct class types for different template arguments.
typenameandclassare equivalent when declaring a type template parameter.- Nontype template parameters represent compile-time values.
- Template definitions are usually placed in headers because the compiler needs them when instantiating templates.
- Class-template members are generally instantiated only when used.
- Dependent qualified type names usually require the
typenamekeyword. - Template parameters may have default template arguments.
- A member template is a member function that has its own template parameter list.
- A member template cannot be virtual.
- Explicit instantiation can control where a particular template instance is generated.
- Template argument deduction performs fewer conversions than ordinary overload resolution.
- Reference template parameters preserve more type information than value parameters.
- Explicit template arguments are useful when a template parameter cannot be deduced.
- Trailing return types and
decltypeallow return types to depend on function parameters. - Reference collapsing makes deduced
T&¶meters capable of binding to both lvalues and rvalues. std::moveis essentially a cast that enables move-aware overload resolution; it does not move data by itself.std::forward<T>preserves the original value category of a forwarded argument.- Function templates can be overloaded with other templates and ordinary functions.
- A more specialized template can be preferred when multiple template overloads match.
- Variadic templates use parameter packs to represent zero or more parameters.
sizeof...reports the number of elements in a parameter pack.- Pack expansion applies a pattern to every element of a parameter pack.
- Forwarding parameter packs combine
Args&&..., reference collapsing, andstd::forwardto preserve argument types and value categories. - An explicit template specialization supplies a custom definition for specific template arguments.
- A specialization must follow the declaration of its primary template and is not the same thing as defining a new overload.