cpp-templates-metaprogramming

C++ Templates and Metaprogramming

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "cpp-templates-metaprogramming" with this command: npx skills add thebushidocollective/han/thebushidocollective-han-cpp-templates-metaprogramming

C++ Templates and Metaprogramming

Master C++ templates, template metaprogramming, SFINAE, concepts, and compile-time computation. This skill enables you to create generic, type-safe, and highly efficient C++ libraries with compile-time guarantees.

Function Templates

Basic Function Templates

// Simple function template template<typename T> T max(T a, T b) { return (a > b) ? a : b; }

// Usage int i = max(10, 20); // T = int double d = max(3.14, 2.71); // T = double // auto x = max(10, 3.14); // ERROR: can't deduce T

// Multiple template parameters template<typename T, typename U> auto add(T a, U b) -> decltype(a + b) { return a + b; }

auto result = add(5, 3.14); // T = int, U = double, returns double

// C++14: simpler return type deduction template<typename T, typename U> auto multiply(T a, U b) { return a * b; }

Template Specialization

// Primary template template<typename T> T absolute(T value) { return (value < 0) ? -value : value; }

// Full specialization for const char* template<> const char* absolute<const char*>(const char* value) { return value; // Strings don't have absolute value }

// Full specialization for std::string template<> std::string absolute<std::string>(std::string value) { return value; }

// Usage int a = absolute(-5); // Uses primary template const char* b = absolute("test"); // Uses const char* specialization

Function Template Overloading

// Overload 1: Generic template template<typename T> void print(T value) { std::cout << "Generic: " << value << std::endl; }

// Overload 2: Pointer specialization template<typename T> void print(T* ptr) { std::cout << "Pointer: " << *ptr << std::endl; }

// Overload 3: Non-template overload void print(const char* str) { std::cout << "String: " << str << std::endl; }

// Usage int x = 42; print(x); // Overload 1 print(&x); // Overload 2 print("hello"); // Overload 3 (exact match preferred)

Class Templates

Basic Class Templates

// Simple class template template<typename T> class Container { T value;

public: Container(T v) : value(v) {}

T get() const { return value; }
void set(T v) { value = v; }

};

// Usage Container<int> intContainer(42); Container<std::string> strContainer("hello");

// Multiple template parameters template<typename K, typename V> class KeyValuePair { K key; V value;

public: KeyValuePair(K k, V v) : key(k), value(v) {}

K getKey() const { return key; }
V getValue() const { return value; }

};

KeyValuePair<std::string, int> pair("answer", 42);

Template Member Functions

template<typename T> class Array { T* data; size_t size;

public: Array(size_t s) : size(s), data(new T[s]) {} ~Array() { delete[] data; }

// Template member function
template&#x3C;typename Func>
void forEach(Func func) {
    for (size_t i = 0; i &#x3C; size; ++i) {
        func(data[i]);
    }
}

// Template conversion operator
template&#x3C;typename U>
operator Array&#x3C;U>() const {
    Array&#x3C;U> result(size);
    for (size_t i = 0; i &#x3C; size; ++i) {
        result.data[i] = static_cast&#x3C;U>(data[i]);
    }
    return result;
}

};

// Usage Array<int> arr(5); arr.forEach([](int& x) { x *= 2; });

Class Template Specialization

// Primary template template<typename T> class Storage { T data;

public: Storage(T d) : data(d) {} T get() const { return data; } };

// Full specialization for pointers template<typename T> class Storage<T*> { T* data;

public: Storage(T* d) : data(d) {} T* get() const { return data; } T& operator*() { return *data; } };

// Full specialization for bool (bit optimization) template<> class Storage<bool> { unsigned char data : 1;

public: Storage(bool d) : data(d) {} bool get() const { return data; } };

Partial Template Specialization

// Primary template template<typename T, typename U> class Pair { public: T first; U second; void info() { std::cout << "Generic pair" << std::endl; } };

// Partial specialization: both types the same template<typename T> class Pair<T, T> { public: T first; T second; void info() { std::cout << "Same type pair" << std::endl; } };

// Partial specialization: second type is pointer template<typename T, typename U> class Pair<T, U*> { public: T first; U* second; void info() { std::cout << "Second is pointer" << std::endl; } };

// Usage Pair<int, double> p1; // Generic Pair<int, int> p2; // Same type Pair<int, double*> p3; // Second is pointer

Template Parameters

Type Parameters

// Single type parameter template<typename T> class Vector { T* data; };

// Multiple type parameters template<typename T, typename Allocator> class CustomVector { T* data; Allocator alloc; };

// Default type parameters template<typename T, typename Compare = std::less<T>> class Set { Compare comp; public: bool less(const T& a, const T& b) { return comp(a, b); } };

Non-Type Parameters

// Integer non-type parameter template<typename T, size_t N> class Array { T data[N];

public: constexpr size_t size() const { return N; }

T&#x26; operator[](size_t i) { return data[i]; }
const T&#x26; operator[](size_t i) const { return data[i]; }

};

Array<int, 10> arr1; // Array of 10 ints Array<double, 5> arr2; // Array of 5 doubles

// Bool non-type parameter template<typename T, bool IsSorted> class Container { public: void insert(T value) { if constexpr (IsSorted) { insert_sorted(value); } else { insert_unsorted(value); } }

private: void insert_sorted(T value) { /* ... / } void insert_unsorted(T value) { / ... */ } };

// Pointer non-type parameter (C++17) template<auto* Ptr> class StaticWrapper { public: auto& get() { return *Ptr; } };

Template Template Parameters

// Template template parameter template<typename T, template<typename> class Container> class Stack { Container<T> data;

public: void push(const T& value) { data.push_back(value); }

T pop() {
    T value = data.back();
    data.pop_back();
    return value;
}

};

// Usage Stack<int, std::vector> intStack; Stack<double, std::deque> doubleStack;

// With multiple parameters template<typename T, template<typename, typename> class Container, typename Allocator = std::allocator<T>> class AdvancedStack { Container<T, Allocator> data; };

Variadic Templates

Parameter Packs

// Basic variadic template template<typename... Args> void print(Args... args) { (std::cout << ... << args) << std::endl; // C++17 fold expression }

print(1, 2, 3, "hello", 3.14);

// Get pack size template<typename... Args> constexpr size_t count(Args... args) { return sizeof...(args); }

size_t n = count(1, 2, 3, 4); // 4

// Recursive parameter pack processing (pre-C++17) template<typename T> void print_recursive(T value) { std::cout << value << std::endl; }

template<typename T, typename... Args> void print_recursive(T first, Args... rest) { std::cout << first << " "; print_recursive(rest...); // Recursive call }

Fold Expressions (C++17)

// Unary right fold: (args op ...) template<typename... Args> auto sum(Args... args) { return (args + ...); }

auto result = sum(1, 2, 3, 4, 5); // 15

// Unary left fold: (... op args) template<typename... Args> auto sum_left(Args... args) { return (... + args); }

// Binary right fold: (args op ... op init) template<typename... Args> auto sum_with_init(Args... args) { return (args + ... + 0); }

// Binary left fold: (init op ... op args) template<typename... Args> auto multiply_with_init(Args... args) { return (1 * ... * args); }

// Logical fold expressions template<typename... Args> bool all_true(Args... args) { return (args && ...); }

template<typename... Args> bool any_true(Args... args) { return (args || ...); }

// Comma fold for side effects template<typename... Args> void print_all(Args... args) { (std::cout << ... << args) << std::endl; }

Variadic Class Templates

// Tuple-like class template<typename... Types> class Tuple;

// Base case: empty tuple template<> class Tuple<> {};

// Recursive case template<typename Head, typename... Tail> class Tuple<Head, Tail...> : private Tuple<Tail...> { Head value;

public: Tuple(Head h, Tail... t) : Tuple<Tail...>(t...), value(h) {}

Head&#x26; head() { return value; }
Tuple&#x3C;Tail...>&#x26; tail() { return *this; }

};

// Usage Tuple<int, double, std::string> t(42, 3.14, "hello");

// Variadic template with perfect forwarding template<typename... Args> auto make_unique_custom(Args&&... args) { return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); }

SFINAE (Substitution Failure Is Not An Error)

Basic SFINAE

// Enable if type has begin() method template<typename T> auto process(T container) -> decltype(container.begin(), void()) { std::cout << "Container with begin()" << std::endl; }

// Enable if type is arithmetic template<typename T> auto process(T value) -> typename std::enable_if<std::is_arithmetic<T>::value>::type { std::cout << "Arithmetic type" << std::endl; }

// Usage std::vector<int> vec; process(vec); // First overload process(42); // Second overload

Std::enable_if

// Enable function for integral types only template<typename T> typename std::enable_if<std::is_integral<T>::value, T>::type increment(T value) { return value + 1; }

// C++14: cleaner syntax with enable_if_t template<typename T> std::enable_if_t<std::is_integral<T>::value, T> decrement(T value) { return value - 1; }

// As template parameter (C++14) template<typename T, typename = std::enable_if_t<std::is_floating_point<T>::value>> T half(T value) { return value / 2; }

// Multiple enable_if conditions template<typename T> std::enable_if_t<std::is_pointer<T>::value && !std::is_const<std::remove_pointer_t<T>>::value, void> modify(T ptr) { *ptr = {}; }

Tag Dispatching

// Implementation functions with tags template<typename Iterator> void advance_impl(Iterator& it, int n, std::random_access_iterator_tag) { it += n; // O(1) for random access }

template<typename Iterator> void advance_impl(Iterator& it, int n, std::input_iterator_tag) { while (n--) ++it; // O(n) for input iterators }

// Dispatch function template<typename Iterator> void advance(Iterator& it, int n) { advance_impl(it, n, typename std::iterator_traits<Iterator>::iterator_category()); }

If Constexpr (C++17)

// Replaces many SFINAE use cases template<typename T> auto process(T value) { if constexpr (std::is_integral_v<T>) { return value * 2; } else if constexpr (std::is_floating_point_v<T>) { return value * 3.14; } else if constexpr (std::is_pointer_v<T>) { return *value; } else { return value; } }

// Variadic template with if constexpr template<typename T, typename... Args> void print(T first, Args... rest) { std::cout << first; if constexpr (sizeof...(rest) > 0) { std::cout << ", "; print(rest...); } else { std::cout << std::endl; } }

Concepts (C++20)

Defining Concepts

#include <concepts>

// Simple concept template<typename T> concept Integral = std::is_integral_v<T>;

// Concept with requires expression template<typename T> concept Incrementable = requires(T x) { { ++x } -> std::same_as<T&>; { x++ } -> std::same_as<T>; };

// Compound concept template<typename T> concept Number = std::is_arithmetic_v<T>;

template<typename T> concept SignedNumber = Number<T> && std::is_signed_v<T>;

// Concept with multiple requirements template<typename T> concept Container = requires(T c) { typename T::value_type; typename T::iterator; { c.begin() } -> std::same_as<typename T::iterator>; { c.end() } -> std::same_as<typename T::iterator>; { c.size() } -> std::convertible_to<std::size_t>; };

Using Concepts

// Constrain function template template<Integral T> T add(T a, T b) { return a + b; }

// Requires clause template<typename T> requires Integral<T> T multiply(T a, T b) { return a * b; }

// Abbreviated function template (C++20) auto divide(Integral auto a, Integral auto b) { return a / b; }

// Constrain class template template<Container C> class Processor { C container; public: void process() { for (auto& item : container) { // Process item } } };

// Multiple constraints template<typename T> requires std::is_arithmetic_v<T> && std::is_signed_v<T> T absolute(T value) { return value < 0 ? -value : value; }

Concept Specialization

// Different implementations based on concepts template<typename T> void process(T value) { if constexpr (Integral<T>) { std::cout << "Processing integer: " << value << std::endl; } else if constexpr (std::floating_point<T>) { std::cout << "Processing float: " << value << std::endl; } else { std::cout << "Processing other: " << value << std::endl; } }

// Concept-based overloading void handle(Integral auto value) { std::cout << "Integral: " << value << std::endl; }

void handle(std::floating_point auto value) { std::cout << "Float: " << value << std::endl; }

void handle(Container auto container) { std::cout << "Container of size: " << container.size() << std::endl; }

Type Traits

Standard Type Traits

#include <type_traits>

// Type properties static_assert(std::is_integral_v<int>); static_assert(std::is_floating_point_v<double>); static_assert(std::is_pointer_v<int*>); static_assert(std::is_array_v<int[5]>); static_assert(std::is_const_v<const int>);

// Type relationships static_assert(std::is_same_v<int, int>); static_assert(std::is_base_of_v<Base, Derived>); static_assert(std::is_convertible_v<int, double>);

// Type modifications using NoConst = std::remove_const_t<const int>; // int using NoRef = std::remove_reference_t<int&>; // int using NoPointer = std::remove_pointer_t<int*>; // int using AddConst = std::add_const_t<int>; // const int using AddPointer = std::add_pointer_t<int>; // int*

// Conditional types using Type = std::conditional_t<true, int, double>; // int

Custom Type Traits

// Check if type has size() method template<typename T, typename = void> struct has_size : std::false_type {};

template<typename T> struct has_size<T, std::void_t<decltype(std::declval<T>().size())>> : std::true_type {};

template<typename T> inline constexpr bool has_size_v = has_size<T>::value;

// Usage static_assert(has_size_v<std::vector<int>>); static_assert(!has_size_v<int>);

// Check if type is iterable template<typename T, typename = void> struct is_iterable : std::false_type {};

template<typename T> struct is_iterable<T, std::void_t< decltype(std::declval<T>().begin()), decltype(std::declval<T>().end())>> : std::true_type {};

template<typename T> inline constexpr bool is_iterable_v = is_iterable<T>::value;

Template Metaprogramming

Compile-Time Computation

// Factorial at compile time template<int N> struct Factorial { static constexpr int value = N * Factorial<N - 1>::value; };

template<> struct Factorial<0> { static constexpr int value = 1; };

constexpr int fact5 = Factorial<5>::value; // 120

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

template<> struct Fibonacci<0> { static constexpr int value = 0; };

template<> struct Fibonacci<1> { static constexpr int value = 1; };

constexpr int fib10 = Fibonacci<10>::value; // 55

Type Lists

// Type list definition template<typename... Types> struct TypeList {};

// Get size of type list template<typename List> struct Length;

template<typename... Types> struct Length<TypeList<Types...>> { static constexpr size_t value = sizeof...(Types); };

// Get type at index template<size_t Index, typename List> struct At;

template<size_t Index, typename Head, typename... Tail> struct At<Index, TypeList<Head, Tail...>> : At<Index - 1, TypeList<Tail...>> {};

template<typename Head, typename... Tail> struct At<0, TypeList<Head, Tail...>> { using type = Head; };

// Usage using MyList = TypeList<int, double, char, std::string>; static_assert(Length<MyList>::value == 4); using SecondType = At<1, MyList>::type; // double

CRTP (Curiously Recurring Template Pattern)

// Static polymorphism via CRTP template<typename Derived> class Base { public: void interface() { static_cast<Derived*>(this)->implementation(); }

void common_functionality() {
    std::cout &#x3C;&#x3C; "Common code" &#x3C;&#x3C; std::endl;
}

};

class Derived1 : public Base<Derived1> { public: void implementation() { std::cout << "Derived1 implementation" << std::endl; } };

class Derived2 : public Base<Derived2> { public: void implementation() { std::cout << "Derived2 implementation" << std::endl; } };

// Usage template<typename T> void use(Base<T>& obj) { obj.interface(); // No virtual function overhead }

Derived1 d1; Derived2 d2; use(d1); // Derived1 implementation use(d2); // Derived2 implementation

Expression Templates

// Expression template for lazy evaluation template<typename E> class VecExpression { public: double operator[](size_t i) const { return static_cast<const E&>(*this)[i]; }

size_t size() const {
    return static_cast&#x3C;const E&#x26;>(*this).size();
}

};

class Vec : public VecExpression<Vec> { std::vector<double> data;

public: Vec(size_t n) : data(n) {}

double&#x26; operator[](size_t i) { return data[i]; }
double operator[](size_t i) const { return data[i]; }
size_t size() const { return data.size(); }

template&#x3C;typename E>
Vec&#x26; operator=(const VecExpression&#x3C;E>&#x26; expr) {
    for (size_t i = 0; i &#x3C; size(); ++i) {
        data[i] = expr[i];
    }
    return *this;
}

};

// Addition expression template<typename E1, typename E2> class VecSum : public VecExpression<VecSum<E1, E2>> { const E1& lhs; const E2& rhs;

public: VecSum(const E1& l, const E2& r) : lhs(l), rhs(r) {}

double operator[](size_t i) const {
    return lhs[i] + rhs[i];
}

size_t size() const { return lhs.size(); }

};

// Operator overload template<typename E1, typename E2> VecSum<E1, E2> operator+(const VecExpression<E1>& lhs, const VecExpression<E2>& rhs) { return VecSum<E1, E2>(static_cast<const E1&>(lhs), static_cast<const E2&>(rhs)); }

// Usage: single loop evaluation Vec v1(1000), v2(1000), v3(1000), v4(1000); v4 = v1 + v2 + v3; // Efficient: no temporary vectors

Constexpr and Consteval

Constexpr Functions

// Constexpr function (can be compile-time or runtime) constexpr int square(int n) { return n * n; }

constexpr int value1 = square(5); // Compile-time int x = 5; int value2 = square(x); // Runtime

// Constexpr with complex logic (C++14+) constexpr int fibonacci(int n) { if (n <= 1) return n;

int a = 0, b = 1;
for (int i = 2; i &#x3C;= n; ++i) {
    int temp = a + b;
    a = b;
    b = temp;
}
return b;

}

// Constexpr with std::array constexpr auto make_array() { std::array<int, 5> arr{}; for (size_t i = 0; i < arr.size(); ++i) { arr[i] = i * i; } return arr; }

constexpr auto squares = make_array();

Consteval Functions (C++20)

// Must be evaluated at compile-time consteval int cube(int n) { return n * n * n; }

constexpr int value3 = cube(5); // OK: compile-time // int y = 5; // int value4 = cube(y); // ERROR: not compile-time

// is_constant_evaluated constexpr int conditional_compute(int n) { if (std::is_constant_evaluated()) { // Compile-time path return n * n; } else { // Runtime path (might use hardware instructions) return n * n; // Could use intrinsics } }

Template Debugging

Compile-Time Debugging

// Print type at compile time (causes error with type info) template<typename T> struct DebugType;

// DebugType<decltype(value)> debug; // Error shows type

// Static assert for debugging template<typename T> void check_type(T value) { static_assert(std::is_integral_v<T>, "T must be integral"); static_assert(sizeof(T) >= 4, "T must be at least 4 bytes"); }

// Concept for better error messages template<typename T> concept AtLeast4Bytes = sizeof(T) >= 4;

template<AtLeast4Bytes T> void process(T value) { // If T doesn't satisfy concept, clear error message }

Template Error Reduction

// Before C++20: cryptic errors template<typename T> void old_process(T value) { value.size(); // Error if T doesn't have size() }

// C++20: Clear concept-based errors template<typename T> concept HasSize = requires(T t) { { t.size() } -> std::convertible_to<std::size_t>; };

template<HasSize T> void new_process(T value) { value.size(); // Clear error if T doesn't satisfy HasSize }

// Static assert for early error template<typename T> void checked_process(T value) { static_assert(HasSize<T>, "T must have size() method"); value.size(); }

Best Practices

  • Prefer concepts over SFINAE (C++20): Clearer error messages and more readable constraints

  • Use type traits for type inspection: Leverage std::is_same, std::is_integral, etc.

  • Prefer constexpr over template metaprogramming: More readable and debuggable

  • Use if constexpr for conditional compilation: Replaces many SFINAE use cases

  • Avoid deep template recursion: Can cause long compile times and errors

  • Use abbreviated function templates carefully: Can hide important type information

  • Provide clear error messages: Use static_assert or concepts to guide users

  • Forward perfectly with std::forward: Preserve value categories in template code

  • Use variadic templates for flexible interfaces: Better than overload sets

  • Document template requirements: Specify what operations types must support

Common Pitfalls

  • Two-phase lookup issues: Name lookup behaves differently in templates

  • Dependent name resolution: Must use typename and template keywords correctly

  • Template instantiation bloat: Each instantiation creates new code

  • Compile-time explosion: Complex metaprogramming can cause long compiles

  • Obscure error messages: Template errors can be difficult to understand

  • Missing typename keyword: Required for dependent type names

  • Missing template keyword: Required for dependent template names

  • Forgetting std::forward: Breaks perfect forwarding

  • Concept subsumption issues: More specific concepts must subsume less specific

  • Constexpr limitations: Not all operations allowed in constexpr context

When to Use

Use this skill when:

  • Creating generic algorithms and data structures

  • Building reusable library code

  • Implementing compile-time computation

  • Constraining template parameters with concepts

  • Performing type introspection and manipulation

  • Optimizing performance with zero-cost abstractions

  • Creating domain-specific embedded languages (DSELs)

  • Implementing static polymorphism with CRTP

  • Building expression template libraries

  • Teaching or learning advanced C++ techniques

Resources

  • C++ Reference - Templates

  • C++ Reference - SFINAE

  • C++ Reference - Concepts

  • C++ Reference - Type Traits

  • C++ Reference - Fold Expressions

  • C++ Reference - Constexpr

  • C++ Templates: The Complete Guide by Vandevoorde, Josuttis, Gregor

  • Modern C++ Design by Andrei Alexandrescu

  • CppCon Talks on Template Metaprogramming

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

typescript-type-system

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

typescript-async-patterns

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

c-systems-programming

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

typescript-utility-types

No summary provided by upstream source.

Repository SourceNeeds Review