Template Metaprogramming (TMP) is a technique in C++ where templates are used to perform computation at Compile-Time. The compiler acts as an interpreter: template instantiation is Turing-complete, so any computable function can (in principle) be evaluated before the program runs.
The core idea is that types and constant expressions are the “values” of a compile-time language, and template specialization is its “branching.”
A type trait is a Template Metaprogramming pattern in C++. It is a template struct that exposes a Compile-Time fact about a type, typically through a static constexpr member called value or a member type alias called type. It is equivalent to Rust’s trait bounds.
Example
// Is T a pointer?template <typename T>struct is_pointer { static constexpr bool value = false;};// Partial specialization: T* is a pointertemplate <typename T>struct is_pointer<T*> { static constexpr bool value = true;};static_assert(is_pointer<int>::value == false);static_assert(is_pointer<int*>::value == true);
The standard library provides dozens of these in <type_traits>: std::is_integral, std::is_same, std::remove_const, std::decay, etc.
Convenience Aliases
By convention, traits that produce a type have a _t alias, and traits that produce a value have a _v alias:
Substitution Failure Is Not An Error (SFINAE) is a Template Metaprogramming rule in C++: when the compiler substitutes template arguments and the result is ill-formed, that specialization is silently discarded from the overload set rather than producing a compile error.
Warning
SFINAE has largely been superceded by if constexpr and Concepts. Use with caution!
Example
// Only enabled when T is an integral typetemplate <typename T>auto to_string(T value) -> std::enable_if_t<std::is_integral_v<T>, std::string> { return std::to_string(value);}// Only enabled when T has a .str() methodtemplate <typename T>auto to_string(T value) -> decltype(value.str()) { return value.str();}
If T = double, the first overload’s enable_if_t produces an invalid type and is discarded. If T = int, the second overload’s decltype(value.str()) is ill-formed and is discarded. No error, the compiler just picks whichever overload survives.
enable_if
The standard mechanism for SFINAE:
template <bool Condition, typename T = void>struct enable_if {}; // no 'type' member when falsetemplate <typename T>struct enable_if<true, T> { using type = T; // 'type' exists only when true};
A Variadic Template Parameter Pack is a Template Metaprogramming pattern in C++ that lets a function accept any number of arguments of any types, resolved at Compile-Time.
Syntax
template <typename... Args>void foo(Args... args) { bar(args...); // pack expansion -- forwards all args to bar}
typename… Args declares a template parameter pack (zero or more types)
Args… args declares a function parameter pack (one parameter per type in Args)
args… is a pack expansion (the compiler substitutes each element)
This is zero-cost: no runtime dispatch, no type erasure. The compiler produces exactly the code you’d write by hand for each combination of argument types.
Patterns
Fold
template <typename... Args>auto sum(Args... args) { return (args + ...); // right fold: a + (b + (c + d))}
sizeof
template <typename... Args>constexpr std::size_t count(Args...) { return sizeof...(Args); // number of types in the pack}
In C++, a Curiously Recurring Template Pattern is an idiomatic approach to achieving Static Polymorphism, where a class X derives from a class Template instantiation using X itself as the template argument.