Skip to main content

cpp-templates-metaprogramming

Use when C++ templates and metaprogramming including template specialization, SFINAE, type traits, and C++20 concepts.

Source facts

Repository
TheBushidoCollective/han
Last source activity
February 11, 2026 at 17:47
Detected SKILL.md language
English
Stars
198
Forks
21

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
cpp-templates-metaprogramming
user-invocable
false
description
Use when C++ templates and metaprogramming including template specialization, SFINAE, type traits, and C++20 concepts.
allowed-tools
["Read","Write","Edit","Grep","Glob","Bash"]
# C++ Templates and Metaprogramming Template metaprogramming enables compile-time computation and code generation, creating flexible, efficient abstractions without runtime overhead. This skill covers function and class templates, specialization, SFINAE, type traits, and modern concepts-based template constraints. ## Function Templates Function templates enable writing generic algorithms that work with any type satisfying requirements. ```cpp #include <iostream> #include <vector> #include <string> // Basic function template template<typename T> T maximum(T a, T b) { return (a > b) ? a : b; } // Multiple template parameters template<typename T, typename U> auto add(T a, U b) -> decltype(a + b) { return a + b; } // Template with non-type parameters template<typename T, size_t N> size_t array_size(T (&)[N]) { return N; } // Template overloading template<typename T> void print(T value) { std::cout << value << "\n"; } template<typename T> void print(const std::vector<T>& vec) { for (const auto& item : vec) { std::cout << item << " "; } std::cout << "\n"; } void function_template_examples() { auto max_int = maximum(10, 20); auto max_double = maximum(3.14, 2.71); auto max_string = maximum(std::string("abc"), std::string("xyz")); auto sum = add(5, 3.14); // int + double int arr[] = {1, 2, 3, 4, 5}; std::cout << "Array size: " << array_size(arr) << "\n"; print(42); print(std::vector<int>{1, 2, 3}); } ``` ## Class Templates Class templates enable creating generic containers and data structures. ```cpp #include <iostream> #include <stdexcept> // Basic class template template<typename T> class Stack { T* data_; size_t size_; size_t capacity_; public: Stack(size_t capacity = 10) : data_(new T[capacity]) , size_(0) , capacity_(capacity) {} ~Stack() { delete[] data_; } void push(const T& value) { if (size_ >= capacity_) { resize(); } data_[size_++] = value; } T pop() { if (size_ == 0) { throw std::underflow_error("Stack is empty"); } return data_[--size_]; } bool empty() const { return size_ == 0; } size_t size() const { return size_; } private: void resize() { capacity_ *= 2; T* new_data = new T[capacity_]; for (size_t i = 0; i < size_; ++i) { new_data[i] = data_[i]; } delete[] data_; data_ = new_data; } }; // Multiple template parameters template<typename Key, typename Value> class Pair { Key key_; Value value_; public: Pair(const Key& k, const Value& v) : key_(k), value_(v) {} const Key& key() const { return key_; } const Value& value() const { return value_; } }; // Template with default parameters template<typename T, typename Allocator = std::allocator<T>> class Vector { // Implementation }; void class_template_examples() { Stack<int> int_stack; int_stack.push(1); int_stack.push(2); std::cout << int_stack.pop() << "\n"; Stack<std::string> str_stack; str_stack.push("hello"); Pair<std::string, int> p("age", 30); } ``` ## Template Specialization Template specialization allows providing custom implementations for specific types. ```cpp #include <iostream> #include <cstring> // Primary template template<typename T> class Container { T value_; public: Container(const T& value) : value_(value) {} void print() const { std::cout << "Generic: " << value_ << "\n"; } size_t memory_size() const { return sizeof(T); } }; // Full specialization for const char* template<> class Container<const char*> { const char* value_; public: Container(const char* value) : value_(value) {} void print() const { std::cout << "C-string: " << value_ << "\n"; } size_t memory_size() const { return std::strlen(value_) + 1; } }; // Partial specialization for pointers template<typename T> class Container<T*> { T* value_; public: Container(T* value) : value_(value) {} void print() const { std::cout << "Pointer: " << *value_ << "\n"; } size_t memory_size() const { return sizeof(T*); } }; // Function template specialization template<typename T> bool is_negative(T value) { return value < 0; } template<> bool is_negative<bool>(bool value) { return false; // bool can't be negative } void specialization_examples() { Container<int> c1(42); c1.print(); // Generic Container<const char*> c2("hello"); c2.print(); // C-string int x = 10; Container<int*> c3(&x); c3.print(); // Pointer } ``` ## SFINAE (Substitution Failure Is Not An Error) SFINAE enables compile-time function selection based on type properties. ```cpp #include <iostream> #include <type_traits> #include <vector> // Enable if type has begin() and end() template<typename T> typename std::enable_if< std::is_same< decltype(std::declval<T>().begin()), decltype(std::declval<T>().end()) >::value >::type print_container(const T& container) { std::cout << "Container: "; for (const auto& item : container) { std::cout << item << " "; } std::cout << "\n"; } // Enable if type is arithmetic template<typename T> typename std::enable_if<std::is_arithmetic<T>::value>::type print_value(T value) { std::cout << "Number: " << value << "\n"; } // Enable if type is not arithmetic template<typename T> typename std::enable_if<!std::is_arithmetic<T>::value>::type print_value(const T& value) { std::cout << "Non-number: " << value << "\n"; } // Using std::enable_if as template parameter template<typename T, typename = std::enable_if_t<std::is_integral<T>::value>> T safe_divide(T a, T b) { if (b == 0) { throw std::domain_error("Division by zero"); } return a / b; } // Tag dispatching (alternative to SFINAE) template<typename T> void process_impl(T value, std::true_type /* is_pointer */) { std::cout << "Processing pointer: " << *value << "\n"; } template<typename T> void process_impl(T value, std::false_type /* is_pointer */) { std::cout << "Processing value: " << value << "\n"; } template<typename T> void process(T value) { process_impl(value, std::is_pointer<T>{}); } void sfinae_examples() { std::vector<int> vec{1, 2, 3}; print_container(vec); print_value(42); print_value(std::string("hello")); std::cout << safe_divide(10, 2) << "\n"; int x = 100; process(x); process(&x); } ``` ## Type Traits Type traits provide compile-time type information and transformations. ```cpp #include <type_traits> #include <iostream> #include <string> // Using standard type traits template<typename T> void analyze_type() { std::cout << "Type analysis:\n"; std::cout << " Is integral: " << std::is_integral<T>::value << "\n"; std::cout << " Is floating point: " << std::is_floating_point<T>::value << "\n"; std::cout << " Is pointer: " << std::is_pointer<T>::value << "\n"; std::cout << " Is const: " << std::is_const<T>::value << "\n"; std::cout << " Size: " << sizeof(T) << "\n"; } // Type transformations template<typename T> void transform_type() { using NoCV = std::remove_cv_t<T>; using NoRef = std::remove_reference_t<T>; using NoPtr = std::remove_pointer_t<T>; using AddConst = std::add_const_t<T>; using AddLRef = std::add_lvalue_reference_t<T>; std::cout << "Is same after remove_cv: " << std::is_same<NoCV, T>::value << "\n"; } // Custom type trait template<typename T> struct is_string : std::false_type {}; template<> struct is_string<std::string> : std::true_type {}; template<> struct is_string<const char*> : std::true_type {}; template<typename T> inline constexpr bool is_string_v = is_string<T>::value; // Conditional types template<typename T> using MakeUnsigned = std::conditional_t< std::is_signed<T>::value, std::make_unsigned_t<T>, T >; // Compile-time if (C++17) template<typename T> void print_type(const T& value) { if constexpr (std::is_integral_v<T>) { std::cout << "Integer: " << value << "\n"; } else if constexpr (std::is_floating_point_v<T>) { std::cout << "Float: " << value << "\n"; } else if constexpr (is_string_v<T>) { std::cout << "String: " << value << "\n"; } else { std::cout << "Unknown type\n"; } } void type_traits_examples() { analyze_type<int>(); analyze_type<const double*>(); print_type(42); print_type(3.14); print_type(std::string("hello")); } ``` ## Variadic Templates Variadic templates enable functions and classes accepting any number of arguments. ```cpp #include <iostream> #include <sstream> // Base case void print_all() { std::cout << "\n"; } // Recursive variadic template template<typename T, typename... Args>
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub