--- name: cpp-templates-metaprogramming description: Use when creating generic and type-safe C++ libraries with templates, SFINAE, concepts, and compile-time metaprogramming. allowed-tools: - Bash - Read - Write - Edit --- # 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 ```cpp // Simple function template template 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 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 auto multiply(T a, U b) { return a * b; } ``` ### Template Specialization ```cpp // Primary template template T absolute(T value) { return (value < 0) ? -value : value; } // Full specialization for const char* template<> const char* absolute(const char* value) { return value; // Strings don't have absolute value } // Full specialization for std::string template<> std::string absolute(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 ```cpp // Overload 1: Generic template template void print(T value) { std::cout << "Generic: " << value << std::endl; } // Overload 2: Pointer specialization template 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 ```cpp // Simple class template template class Container { T value; public: Container(T v) : value(v) {} T get() const { return value; } void set(T v) { value = v; } }; // Usage Container intContainer(42); Container strContainer("hello"); // Multiple template parameters template 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 pair("answer", 42); ``` ### Template Member Functions ```cpp template 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 void forEach(Func func) { for (size_t i = 0; i < size; ++i) { func(data[i]); } } // Template conversion operator template operator Array() const { Array result(size); for (size_t i = 0; i < size; ++i) { result.data[i] = static_cast(data[i]); } return result; } }; // Usage Array arr(5); arr.forEach([](int& x) { x *= 2; }); ``` ### Class Template Specialization ```cpp // Primary template template class Storage { T data; public: Storage(T d) : data(d) {} T get() const { return data; } }; // Full specialization for pointers template class Storage { 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 { unsigned char data : 1; public: Storage(bool d) : data(d) {} bool get() const { return data; } }; ``` ### Partial Template Specialization ```cpp // Primary template template class Pair { public: T first; U second; void info() { std::cout << "Generic pair" << std::endl; } }; // Partial specialization: both types the same template class Pair { public: T first; T second; void info() { std::cout << "Same type pair" << std::endl; } }; // Partial specialization: second type is pointer template class Pair { public: T first; U* second; void info() { std::cout << "Second is pointer" << std::endl; } }; // Usage Pair p1; // Generic Pair p2; // Same type Pair p3; // Second is pointer ``` ## Template Parameters ### Type Parameters ```cpp // Single type parameter template class Vector { T* data; }; // Multiple type parameters template class CustomVector { T* data; Allocator alloc; }; // Default type parameters template> class Set { Compare comp; public: bool less(const T& a, const T& b) { return comp(a, b); } }; ``` ### Non-Type Parameters ```cpp // Integer non-type parameter template class Array { T data[N]; public: constexpr size_t size() const { return N; } T& operator[](size_t i) { return data[i]; } const T& operator[](size_t i) const { return data[i]; } }; Array arr1; // Array of 10 ints Array arr2; // Array of 5 doubles // Bool non-type parameter template 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 class StaticWrapper { public: auto& get() { return *Ptr; } }; ``` ### Template Template Parameters ```cpp // Template template parameter template class Container> class Stack { Container data; public: void push(const T& value) { data.push_back(value); } T pop() { T value = data.back(); data.pop_back(); return value; } }; // Usage Stack intStack; Stack doubleStack; // With multiple parameters template class Container, typename Allocator = std::allocator> class AdvancedStack { Container data; }; ``` ## Variadic Templates ### Parameter Packs ```cpp // Basic variadic template template void print(Args... args) { (std::cout << ... << args) << std::endl; // C++17 fold expression } print(1, 2, 3, "hello", 3.14); // Get pack size template 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 void print_recursive(T value) { std::cout << value << std::endl; } template void print_recursive(T first, Args... rest) { std::cout << first << " "; print_recursive(rest...); // Recursive call } ``` ### Fold Expressions (C++17) ```cpp // Unary right fold: (args op ...) template auto sum(Args... args) { return (args + ...); } auto result = sum(1, 2, 3, 4, 5); // 15 // Unary left fold: (... op args) template auto sum_left(Args... args) { return (... + args); } // Binary right fold: (args op ... op init) template auto sum_with_init(Args... args) { return (args + ... + 0); } // Binary left fold: (init op ... op args) template auto multiply_with_init(Args... args) { return (1 * ... * args); } // Logical fold expressions template bool all_true(Args... args) { return (args && ...); } template bool any_true(Args... args) { return (args || ...); } // Comma fold for side effects template void print_all(Args... args) { (std::cout << ... << args) << std::endl; } ``` ### Variadic Class Templates ```cpp // Tuple-like class template class Tuple; // Base case: empty tuple template<> class Tuple<> {}; // Recursive case template class Tuple : private Tuple { Head value; public: Tuple(Head h, Tail... t) : Tuple(t...), value(h) {} Head& head() { return value; } Tuple& tail() { return *this; } }; // Usage Tuple t(42, 3.14, "hello"); // Variadic template with perfect forwarding template auto make_unique_custom(Args&&... args) { return std::unique_ptr(new T(std::forward(args)...)); } ``` ## SFINAE (Substitution Failure Is Not An Error) ### Basic SFINAE ```cpp // Enable if type has begin() method template auto process(T container) -> decltype(container.begin(), void()) { std::cout << "Container with begin()" << std::endl; } // Enable if type is arithmetic template auto process(T value) -> typename std::enable_if::value>::type { std::cout << "Arithmetic type" << std::endl; } // Usage std::vector vec; process(vec); // First overload process(42); // Second overload ``` ### Std::enable_if ```cpp // Enable function for integral types only template typename std::enable_if::value, T>::type increment(T value) { return value + 1; } // C++14: cleaner syntax with enable_if_t template std::enable_if_t::value, T> decrement(T value) { return value - 1; } // As template parameter (C++14) template::value>> T half(T value) { return value / 2; } // Multiple enable_if conditions template std::enable_if_t::value && !std::is_const>::value, void> modify(T ptr) { *ptr = {}; } ``` ### Tag Dispatching ```cpp // Implementation functions with tags template void advance_impl(Iterator& it, int n, std::random_access_iterator_tag) { it += n; // O(1) for random access } template void advance_impl(Iterator& it, int n, std::input_iterator_tag) { while (n--) ++it; // O(n) for input iterators } // Dispatch function template void advance(Iterator& it, int n) { advance_impl(it, n, typename std::iterator_traits::iterator_category()); } ``` ### If Constexpr (C++17) ```cpp // Replaces many SFINAE use cases template auto process(T value) { if constexpr (std::is_integral_v) { return value * 2; } else if constexpr (std::is_floating_point_v) { return value * 3.14; } else if constexpr (std::is_pointer_v) { return *value; } else { return value; } } // Variadic template with if constexpr template 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 ```cpp #include // Simple concept template concept Integral = std::is_integral_v; // Concept with requires expression template concept Incrementable = requires(T x) { { ++x } -> std::same_as; { x++ } -> std::same_as; }; // Compound concept template concept Number = std::is_arithmetic_v; template concept SignedNumber = Number && std::is_signed_v; // Concept with multiple requirements template concept Container = requires(T c) { typename T::value_type; typename T::iterator; { c.begin() } -> std::same_as; { c.end() } -> std::same_as; { c.size() } -> std::convertible_to; }; ``` ### Using Concepts ```cpp // Constrain function template template T add(T a, T b) { return a + b; } // Requires clause template requires Integral 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 class Processor { C container; public: void process() { for (auto& item : container) { // Process item } } }; // Multiple constraints template requires std::is_arithmetic_v && std::is_signed_v T absolute(T value) { return value < 0 ? -value : value; } ``` ### Concept Specialization ```cpp // Different implementations based on concepts template void process(T value) { if constexpr (Integral) { std::cout << "Processing integer: " << value << std::endl; } else if constexpr (std::floating_point) { 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 ```cpp #include // Type properties static_assert(std::is_integral_v); static_assert(std::is_floating_point_v); static_assert(std::is_pointer_v); static_assert(std::is_array_v); static_assert(std::is_const_v); // Type relationships static_assert(std::is_same_v); static_assert(std::is_base_of_v); static_assert(std::is_convertible_v); // Type modifications using NoConst = std::remove_const_t; // int using NoRef = std::remove_reference_t; // int using NoPointer = std::remove_pointer_t; // int using AddConst = std::add_const_t; // const int using AddPointer = std::add_pointer_t; // int* // Conditional types using Type = std::conditional_t; // int ``` ### Custom Type Traits ```cpp // Check if type has size() method template struct has_size : std::false_type {}; template struct has_size().size())>> : std::true_type {}; template inline constexpr bool has_size_v = has_size::value; // Usage static_assert(has_size_v>); static_assert(!has_size_v); // Check if type is iterable template struct is_iterable : std::false_type {}; template struct is_iterable().begin()), decltype(std::declval().end())>> : std::true_type {}; template inline constexpr bool is_iterable_v = is_iterable::value; ``` ## Template Metaprogramming ### Compile-Time Computation ```cpp // Factorial at compile time template struct Factorial { static constexpr int value = N * Factorial::value; }; template<> struct Factorial<0> { static constexpr int value = 1; }; constexpr int fact5 = Factorial<5>::value; // 120 // Fibonacci at compile time template struct Fibonacci { static constexpr int value = Fibonacci::value + Fibonacci::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 ```cpp // Type list definition template struct TypeList {}; // Get size of type list template struct Length; template struct Length> { static constexpr size_t value = sizeof...(Types); }; // Get type at index template struct At; template struct At> : At> {}; template struct At<0, TypeList> { using type = Head; }; // Usage using MyList = TypeList; static_assert(Length::value == 4); using SecondType = At<1, MyList>::type; // double ``` ### CRTP (Curiously Recurring Template Pattern) ```cpp // Static polymorphism via CRTP template class Base { public: void interface() { static_cast(this)->implementation(); } void common_functionality() { std::cout << "Common code" << std::endl; } }; class Derived1 : public Base { public: void implementation() { std::cout << "Derived1 implementation" << std::endl; } }; class Derived2 : public Base { public: void implementation() { std::cout << "Derived2 implementation" << std::endl; } }; // Usage template void use(Base& obj) { obj.interface(); // No virtual function overhead } Derived1 d1; Derived2 d2; use(d1); // Derived1 implementation use(d2); // Derived2 implementation ``` ### Expression Templates ```cpp // Expression template for lazy evaluation template class VecExpression { public: double operator[](size_t i) const { return static_cast(*this)[i]; } size_t size() const { return static_cast(*this).size(); } }; class Vec : public VecExpression { std::vector data; public: Vec(size_t n) : data(n) {} double& operator[](size_t i) { return data[i]; } double operator[](size_t i) const { return data[i]; } size_t size() const { return data.size(); } template Vec& operator=(const VecExpression& expr) { for (size_t i = 0; i < size(); ++i) { data[i] = expr[i]; } return *this; } }; // Addition expression template class VecSum : public VecExpression> { 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 VecSum operator+(const VecExpression& lhs, const VecExpression& rhs) { return VecSum(static_cast(lhs), static_cast(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 ```cpp // 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 <= n; ++i) { int temp = a + b; a = b; b = temp; } return b; } // Constexpr with std::array constexpr auto make_array() { std::array 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) ```cpp // 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 ```cpp // Print type at compile time (causes error with type info) template struct DebugType; // DebugType debug; // Error shows type // Static assert for debugging template void check_type(T value) { static_assert(std::is_integral_v, "T must be integral"); static_assert(sizeof(T) >= 4, "T must be at least 4 bytes"); } // Concept for better error messages template concept AtLeast4Bytes = sizeof(T) >= 4; template void process(T value) { // If T doesn't satisfy concept, clear error message } ``` ### Template Error Reduction ```cpp // Before C++20: cryptic errors template void old_process(T value) { value.size(); // Error if T doesn't have size() } // C++20: Clear concept-based errors template concept HasSize = requires(T t) { { t.size() } -> std::convertible_to; }; template void new_process(T value) { value.size(); // Clear error if T doesn't satisfy HasSize } // Static assert for early error template void checked_process(T value) { static_assert(HasSize, "T must have size() method"); value.size(); } ``` ## Best Practices 1. **Prefer concepts over SFINAE (C++20)**: Clearer error messages and more readable constraints 2. **Use type traits for type inspection**: Leverage std::is_same, std::is_integral, etc. 3. **Prefer constexpr over template metaprogramming**: More readable and debuggable 4. **Use if constexpr for conditional compilation**: Replaces many SFINAE use cases 5. **Avoid deep template recursion**: Can cause long compile times and errors 6. **Use abbreviated function templates carefully**: Can hide important type information 7. **Provide clear error messages**: Use static_assert or concepts to guide users 8. **Forward perfectly with std::forward**: Preserve value categories in template code 9. **Use variadic templates for flexible interfaces**: Better than overload sets 10. **Document template requirements**: Specify what operations types must support ## Common Pitfalls 1. **Two-phase lookup issues**: Name lookup behaves differently in templates 2. **Dependent name resolution**: Must use typename and template keywords correctly 3. **Template instantiation bloat**: Each instantiation creates new code 4. **Compile-time explosion**: Complex metaprogramming can cause long compiles 5. **Obscure error messages**: Template errors can be difficult to understand 6. **Missing typename keyword**: Required for dependent type names 7. **Missing template keyword**: Required for dependent template names 8. **Forgetting std::forward**: Breaks perfect forwarding 9. **Concept subsumption issues**: More specific concepts must subsume less specific 10. **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](https://en.cppreference.com/w/cpp/language/templates) - [C++ Reference - SFINAE](https://en.cppreference.com/w/cpp/language/sfinae) - [C++ Reference - Concepts](https://en.cppreference.com/w/cpp/language/constraints) - [C++ Reference - Type Traits](https://en.cppreference.com/w/cpp/header/type_traits) - [C++ Reference - Fold Expressions](https://en.cppreference.com/w/cpp/language/fold) - [C++ Reference - Constexpr](https://en.cppreference.com/w/cpp/language/constexpr) - [C++ Templates: The Complete Guide by Vandevoorde, Josuttis, Gregor](http://www.tmplbook.com/) - [Modern C++ Design by Andrei Alexandrescu](https://erdani.com/index.php/books/modern-c-design/) - [CppCon Talks on Template Metaprogramming](https://www.youtube.com/user/CppCon)