Template

Generics

In C++, templates are a way to make functions and classes generic, so that they can handle any data type. This happens at compile time (static dispatch).

// `T` is a type parameter, it can be any type
template <typename T>
void swap(T& a, T& b) {
    T temp = a;
    a = b;
    b = temp;
}
int a = 1, b = 2;
swap<int>(a, b);
double c = 1.1, d = 2.2;
swap<double>(c, d);
// multiple type parameters also possible
template <typename T, typename U>
class Pair { /* ... */ };
Pair<int, double> p;
Pair<int, int> q;

It's possible to create specialized versions of a template for specific types.

// generic version
template <typename T>
void print(const T& value) { /* ... */ }

// specialized version for `std::string`
template <>
void print<std::string>(const std::string& value) { /* ... */ }

Variadic Templates

Variadic templates allow a function or class to accept any number of arguments of any type.

// base case
void print() { std::cout << std::endl; }
// recursive case
template <typename T, typename... Args>
void print(const T& first_arg, const Args&... args) {
    std::cout << first_arg << ' ';
    print(args...);
}
print(1, 2.2, "three");
// `sizeof...(args)` gives the number of arguments
template <typename... Args>
void count_args(const Args&... args) {
    std::cout << sizeof...(args) << std::endl;
}
count_args(1, 2.2, "three"); // => 3

Meta-programming

constexpr is a keyword used to declare a constant expression. It indicates that a variable or function can be evaluated at compile-time and its value can be used in other compile-time expressions.

// constexpr variables
constexpr int length = 10;
constexpr int width = 4 + 6;

// constexpr functions
constexpr int factorial(int n) {
    return n <= 1 ? 1 : (n * factorial(n - 1));
}

We can use meta-programming techniques to perform calculations and generate compile-time constants or data structures to optimize code or providing compile-time configuration options.

// compile-time Fibonacci sequence
template <int N>
struct Fibonacci {
    static const int value = Fibonacci<N - 1>::value + Fibonacci<N - 2>::value;
};
template <> struct Fibonacci<0> { static const int value = 0; };
template <> struct Fibonacci<1> { static const int value = 1; };

// usage
int fib = Fibonacci<10>::value; // => 55