본문으로 건너뛰기

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

  1. Templates are the foundation of generic programming in C++.
  2. A function template generates functions for deduced or explicit template arguments.
  3. A class template generates distinct class types for different template arguments.
  4. typename and class are equivalent when declaring a type template parameter.
  5. Nontype template parameters represent compile-time values.
  6. Template definitions are usually placed in headers because the compiler needs them when instantiating templates.
  7. Class-template members are generally instantiated only when used.
  8. Dependent qualified type names usually require the typename keyword.
  9. Template parameters may have default template arguments.
  10. A member template is a member function that has its own template parameter list.
  11. A member template cannot be virtual.
  12. Explicit instantiation can control where a particular template instance is generated.
  13. Template argument deduction performs fewer conversions than ordinary overload resolution.
  14. Reference template parameters preserve more type information than value parameters.
  15. Explicit template arguments are useful when a template parameter cannot be deduced.
  16. Trailing return types and decltype allow return types to depend on function parameters.
  17. Reference collapsing makes deduced T&& parameters capable of binding to both lvalues and rvalues.
  18. std::move is essentially a cast that enables move-aware overload resolution; it does not move data by itself.
  19. std::forward<T> preserves the original value category of a forwarded argument.
  20. Function templates can be overloaded with other templates and ordinary functions.
  21. A more specialized template can be preferred when multiple template overloads match.
  22. Variadic templates use parameter packs to represent zero or more parameters.
  23. sizeof... reports the number of elements in a parameter pack.
  24. Pack expansion applies a pattern to every element of a parameter pack.
  25. Forwarding parameter packs combine Args&&..., reference collapsing, and std::forward to preserve argument types and value categories.
  26. An explicit template specialization supplies a custom definition for specific template arguments.
  27. A specialization must follow the declaration of its primary template and is not the same thing as defining a new overload.