Template
A C++ template is a mechanism for creating generic functions and classes that can operate on different data types without duplicating code.
- Template types are often called generic types
T, and programming with templates is known as generic programming. - A placeholder type
Tcan be used for function parameters, return types, and local variables whose actual type will be determined later. - A template parameter declaration
template <typename T>defines the template parameters that can be used within the template.
1. Function Template
A function template defines a family of functions. Syntax:
template <parameter-list>
function-declaration
/// @brief The function template declaration
template <int N, typename T, typename U>
auto max(T x, U y) {
std::cout << "N = " << N << '\n';
return (x < y) ? y : x;
}
void main() {
// Explicit template arguments
auto result1 = max<5, int, double>(2, 3.5);
std::cout << result1 << '\n';
// Template argument deduction
auto result2 = max<10>(4, 5.5);
std::cout << result2 << '\n';
}
template <typename T, typename U, int N>is the template parameter declaration.TandUare generic (placeholder) types.Nis a non-type template parameter representing a compile-time constant value.- Template argument deduction allows the compiler to determine template arguments automatically from the function call.
2. Class Template
A class template is a template definition for instantiating class types (structs, classes, or unions). Class template argument deduction (CTAD) is a C++17 feature that allows the compiler to deduce the template type arguments from an initializer. Syntax:
template <parameter-list>
class-declaration
template <typename T>
struct Pair {
T first{};
T second{};
};
template <typename T>
constexpr T max(Pair<T> p) {
return (p.first < p.second ? p.second : p.first);
}
void main() {
Pair<int> p1{ 5, 6 }; // instantiates Pair<int>
Pair<double> p1{ 5, 6 }; // instantiates Pair<int>
auto result = max<int>(p1);
}
// ===================================================================================
// Compiler
// ===================================================================================
// A declaration for our Pair class template
template <typename T>
struct Pair;
// Explicitly define what Pair<int> looks like
template <> // tells the compiler this is a template type with no template parameters
struct Pair<int> {
int first{};
int second{};
};
// Explicitly define what Pair<double> looks like
template <> // tells the compiler this is a template type with no template parameters
struct Pair<double> {
double first{};
double second{};
};
Alias templates define a family of type aliases.
template <typename T>
using Coord = Pair<T>;
Coord<int> point; // Equivalent to Pair<int>
3. Variadic Templates (C++11)
A variadic template is a template that can accept a variable number of template arguments.
- A template parameter pack is a template parameter that accepts zero or more template arguments (types, non-types, or templates).
- A function parameter pack is a function parameter that accepts zero or more function arguments.
Syntax:
type... pack_name
template <typename... Args>
class MyClass;
template <typename... Args> // template parameter pack
void print(Args... args) {// function parameter pack
(std::cout << ... << args);
}
void main() {
print(1, " ", 2.5, '\n');
}
4. Template Specialization
Template specialization allows customized behavior for specific template arguments.
4.1. Full Template Specialization
A full specialization provides a completely different implementation for a specific type.
template<typename T>
struct Printer {
static void print() {}
};
template<>
struct Printer<int> {
static void print() {}
};
// if (T == int)
// use "specialized version";
// else
// use primary template;
4.2. Partial Template Specialization
A partial specialization customizes a subset of template parameters. Partial specialization is only allowed for class templates.
template<typename T, typename U>
struct Pair {};
template<typename T>
struct Pair<T, int> {};
// if (U == int)
// use "partial specialization";
// else
// use primary template;
5. Type Traits
**Type traits **provide compile-time information about types. <type_traits> defines a series of classes to obtain type information on compile-time.
// https://cplusplus.com/reference/type_traits/
std::is_integral
std::is_floating_point
std::is_pointer
std::is_reference
std::is_const
std::is_same
6. SFINAE - “Substitution Failure Is Not An Error”
This rule applies during overload resolution of function templates:
When substituting the explicitly specified or deduced type for the template parameter fails, the specialization is discarded from the overload set instead of causing a compile error.
This feature is used in template meta-programming.
- Conditional function overloads
- Compile-time type selection
- Template constraints
- Generic library development
// https://en.cppreference.com/cpp/language/sfinae
template<typename T>
typename std::enable_if<std::is_integral<T>::value>::type // default T is void in std::enable_if
foo(T value) {}
void main() {
foo(10); // OK
foo(3.14); // Not considered during overload resolution
}
7. Concepts (C++20)
A concept is a named set of requirements. The definition of a concept must appear at namespace scope.
Concepts provide a more readable way to constrain templates.
Concepts are generally preferred over SFINAE in modern C++ because they produce clearer code and better compiler diagnostics.
Syntax:
cpp template<class T, class U> concept Derived = std::is_base_of<U, T>::value;
https://www.cppstories.com/2021/concepts-intro/