| name | templates |
| version | 3.0.0 |
| description | Production-grade skill for C++ template programming and metaprogramming. Covers function/class templates, variadic templates, SFINAE, concepts, type traits, and compile-time computation. |
| sasmp_version | 1.3.0 |
| skill_version | 3.0.0 |
| bonded_agent | 01-modern-cpp-expert |
| bond_type | PRIMARY_BOND |
| category | development |
| parameters | [object Object] |
| error_handling | [object Object] |
Templates Skill
Production-Grade Development Skill | C++ Template Metaprogramming
Master C++ template programming from basics to advanced metaprogramming techniques.
Template Basics
Function Templates
// Basic function template
template<typename T>
T max(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;
}
// C++20: Abbreviated function template
auto multiply(auto a, auto b) {
return a * b;
}
// Non-type template parameter
template<typename T, std::size_t N>
constexpr std::size_t array_size(T (&)[N]) {
return N;
}
// Template argument deduction
max(1, 2); // T = int
max(1.0, 2.0); // T = double
max<double>(1, 2); // Explicit: T = double
Class Templates
template<typename T, std::size_t Capacity = 64>
class FixedVector {
std::array<T, Capacity> data_;
std::size_t size_ = 0;
public:
void push_back(const T& value) {
if (size_ < Capacity) {
data_[size_++] = value;
}
}
T& operator[](std::size_t idx) { return data_[idx]; }
const T& operator[](std::size_t idx) const { return data_[idx]; }
std::size_t size() const { return size_; }
static constexpr std::size_t capacity() { return Capacity; }
};
// Class Template Argument Deduction (CTAD) - C++17
FixedVector vec{1, 2, 3}; // Deduces FixedVector<int, 3>
// Deduction guide
template<typename T, typename... Args>
FixedVector(T, Args...) -> FixedVector<T, 1 + sizeof...(Args)>;
Template Specialization
// Primary template
template<typename T>
struct TypeInfo {
static constexpr const char* name = "unknown";
};
// Full specialization
template<>
struct TypeInfo<int> {
static constexpr const char* name = "int";
};
template<>
struct TypeInfo<double> {
static constexpr const char* name = "double";
};
// Partial specialization
template<typename T>
struct TypeInfo<std::vector<T>> {
static constexpr const char* name = "vector";
using element_type = T;
};
// Partial specialization for pointers
template<typename T>
struct TypeInfo<T*> {
static constexpr const char* name = "pointer";
using pointed_type = T;
};
Variadic Templates
Parameter Packs
// Variadic function template
template<typename... Args>
void print(Args... args) {
((std::cout << args << ' '), ...); // Fold expression (C++17)
std::cout << '\n';
}
// Sizeof... operator
template<typename... Args>
constexpr std::size_t count_args() {
return sizeof...(Args);
}
// Recursive unpacking (pre-C++17)
template<typename T>
void print_recursive(T t) {
std::cout << t << '\n';
}
template<typename T, typename... Rest>
void print_recursive(T first, Rest... rest) {
std::cout << first << ' ';
print_recursive(rest...);
}
Fold Expressions (C++17)
// Unary right fold: (pack op ...)
template<typename... Args>
auto sum(Args... args) {
return (args + ...); // ((a + b) + c) + d...
}
// Unary left fold: (... op pack)
template<typename... Args>
auto sum_left(Args... args) {
return (... + args); // a + (b + (c + d...))
}
// Binary fold with init
template<typename... Args>
auto sum_with_init(Args... args) {
return (0 + ... + args); // 0 + a + b + c...
}
// Logical folds
template<typename... Args>
bool all(Args... args) {
return (... && args); // All true
}
template<typename... Args>
bool any(Args... args) {
return (... || args); // Any true
}
// Comma fold for side effects
template<typename F, typename... Args>
void for_each_arg(F f, Args&&... args) {
(f(std::forward<Args>(args)), ...);
}
SFINAE and enable_if
SFINAE Basics
#include <type_traits>
// Enable only for integral types
template<typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type
safe_divide(T a, T b) {
return b != 0 ? a / b : 0;
}
// C++14 style
template<typename T>
std::enable_if_t<std::is_floating_point_v<T>, T>
safe_divide(T a, T b) {
return b != T{0} ? a / b : std::numeric_limits<T>::quiet_NaN();
}
// Using void_t for detection idiom
template<typename, 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 {};
// Usage
static_assert(has_size<std::vector<int>>::value);
static_assert(!has_size<int>::value);
Detection Idiom
// is_detected implementation
template<typename, template<typename...> class, typename...>
struct is_detected_impl : std::false_type {};
template<template<typename...> class Op, typename... Args>
struct is_detected_impl<std::void_t<Op<Args...>>, Op, Args...>
: std::true_type {};
template<template<typename...> class Op, typename... Args>
using is_detected = is_detected_impl<void, Op, Args...>;
// Detection expressions
template<typename T>
using has_begin_t = decltype(std::declval<T>().begin());
template<typename T>
using has_end_t = decltype(std::declval<T>().end());
template<typename T>
constexpr bool is_container_v =
is_detected<has_begin_t, T>::value &&
is_detected<has_end_t, T>::value;
Concepts (C++20)
Standard Concepts
#include <concepts>
// Using standard concepts
template<std::integral T>
T gcd(T a, T b) {
while (b != 0) {
T t = b;
b = a % b;
a = t;
}
return a;
}
// Common standard concepts
template<std::floating_point T>
T sqrt_approx(T x);
template<std::copyable T>
void process(T value);
template<std::invocable<int> F>
void apply(F&& func);
template<std::ranges::range R>
void iterate(R&& range);
Custom Concepts
// Define concept with requires expression
template<typename T>
concept Hashable = requires(T a) {
{ std::hash<T>{}(a) } -> std::convertible_to<std::size_t>;
};
template<typename T>
concept Printable = requires(std::ostream& os, T value) {
{ os << value } -> std::same_as<std::ostream&>;
};
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>;
};
// Compound concepts
template<typename T>
concept Numeric = std::integral<T> || std::floating_point<T>;
// Use in function
template<Container C>
void process(const C& container) {
for (const auto& item : container) {
// ...
}
}
// Constrained auto
void print_hashable(Hashable auto const& value) {
std::cout << std::hash<std::decay_t<decltype(value)>>{}(value);
}
Requires Clauses
// Requires clause in template
template<typename T>
requires std::is_arithmetic_v<T>
T average(std::span<const T> values) {
return std::reduce(values.begin(), values.end()) / values.size();
}
// Trailing requires
template<typename T>
T* find(T* first, T* last, const T& value)
requires std::equality_comparable<T>
{
while (first != last && *first != value) {
++first;
}
return first;
}
// Concept subsumption for overloading
template<typename T>
void process(T val) requires std::integral<T> {
std::cout << "integral\n";
}
template<typename T>
void process(T val) requires std::signed_integral<T> { // More specific
std::cout << "signed integral\n";
}
Type Traits
Standard Type Traits
#include <type_traits>
// Type categories
static_assert(std::is_integral_v<int>);
static_assert(std::is_floating_point_v<double>);
static_assert(std::is_class_v<std::string>);
static_assert(std::is_pointer_v<int*>);
// Type properties
static_assert(std::is_const_v<const int>);
static_assert(std::is_trivially_copyable_v<int>);
static_assert(std::is_default_constructible_v<std::string>);
// Type transformations
using NoConst = std::remove_const_t<const int>; // int
using Pointer = std::add_pointer_t<int>; // int*
using Decayed = std::decay_t<int&>; // int
using Common = std::common_type_t<int, double>; // double
// Conditional type
template<typename T>
using storage_type = std::conditional_t<
sizeof(T) <= sizeof(void*),
T, // Small: store by value
std::unique_ptr<T> // Large: store by pointer
>;
Custom Type Traits
// Custom type trait
template<typename T>
struct is_smart_pointer : std::false_type {};
template<typename T>
struct is_smart_pointer<std::unique_ptr<T>> : std::true_type {};
template<typename T>
struct is_smart_pointer<std::shared_ptr<T>> : std::true_type {};
template<typename T>
constexpr bool is_smart_pointer_v = is_smart_pointer<T>::value;
// Type transformation
template<typename T>
struct remove_all_pointers {
using type = T;
};
template<typename T>
struct remove_all_pointers<T*> {
using type = typename remove_all_pointers<T>::type;
};
template<typename T>
using remove_all_pointers_t = typename remove_all_pointers<T>::type;
// Usage
static_assert(std::is_same_v<remove_all_pointers_t<int***>, int>);
Compile-Time Computation
constexpr Functions
// Compile-time factorial
constexpr int factorial(int n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}
static_assert(factorial(5) == 120);
// constexpr class
class ConstexprString {
const char* data_;
std::size_t size_;
public:
constexpr ConstexprString(const char* str)
: data_(str), size_(0) {
while (str[size_] != '\0') ++size_;
}
constexpr std::size_t size() const { return size_; }
constexpr char operator[](std::size_t i) const { return data_[i]; }
};
constexpr auto hello = ConstexprString("Hello");
static_assert(hello.size() == 5);
consteval (C++20)
// Guaranteed compile-time evaluation
consteval int square(int n) {
return n * n;
}
constexpr int x = square(5); // OK: compile-time
// int y = square(runtime_value); // Error: must be compile-time
// Compile-time string hashing
consteval std::size_t hash_string(std::string_view str) {
std::size_t hash = 0;
for (char c : str) {
hash = hash * 31 + static_cast<std::size_t>(c);
}
return hash;
}
// Use in switch
switch (hash_string(input)) {
case hash_string("foo"): /* ... */ break;
case hash_string("bar"): /* ... */ break;
}
Template Error Messages
Better Error Messages with Concepts
// Without concepts - cryptic error
template<typename T>
void sort_container(T& container) {
std::sort(container.begin(), container.end());
}
// Error with int: no member named 'begin' in 'int'
// With concepts - clear error
template<typename T>
concept Sortable = requires(T c) {
{ c.begin() } -> std::random_access_iterator;
{ c.end() } -> std::random_access_iterator;
};
template<Sortable T>
void sort_container(T& container) {
std::sort(container.begin(), container.end());
}
// Error with int: constraints not satisfied [Sortable]
Static Assert for Custom Messages
template<typename T>
class NumericContainer {
static_assert(std::is_arithmetic_v<T>,
"NumericContainer requires an arithmetic type (int, float, etc.)");
// ...
};
// Clear error:
// error: static assertion failed: NumericContainer requires an arithmetic type
Troubleshooting Decision Tree
Template error?
├── "no matching function"
│ ├── Check template parameter deduction
│ ├── Check SFINAE conditions
│ └── Add explicit template arguments
├── "ambiguous call"
│ ├── Make one overload more specific
│ ├── Use concepts for disambiguation
│ └── Add explicit template arguments
├── "incomplete type"
│ ├── Forward declare issue
│ ├── Move implementation to .cpp (explicit instantiation)
│ └── Check circular dependencies
├── "exceeds maximum depth"
│ ├── Add base case to recursion
│ ├── Increase compiler limit
│ └── Use fold expressions instead
└── "constraint not satisfied"
├── Check concept requirements
└── Add missing operations to type
Unit Test Template
#include <gtest/gtest.h>
#include <type_traits>
class TemplateTest : public ::testing::Test {};
// Test type traits
TEST_F(TemplateTest, TypeTraitsWork) {
static_assert(std::is_integral_v<int>);
static_assert(!std::is_integral_v<double>);
static_assert(is_smart_pointer_v<std::unique_ptr<int>>);
}
// Test concepts
TEST_F(TemplateTest, ConceptsSatisfied) {
static_assert(Hashable<int>);
static_assert(Hashable<std::string>);
static_assert(Container<std::vector<int>>);
static_assert(!Container<int>);
}
// Test variadic templates
TEST_F(TemplateTest, VariadicSum) {
EXPECT_EQ(sum(1, 2, 3, 4, 5), 15);
EXPECT_DOUBLE_EQ(sum(1.0, 2.5, 3.5), 7.0);
}
// Test constexpr
TEST_F(TemplateTest, ConstexprComputation) {
constexpr auto result = factorial(5);
EXPECT_EQ(result, 120);
constexpr auto hash = hash_string("test");
EXPECT_NE(hash, 0);
}
// Test template specialization
TEST_F(TemplateTest, Specialization) {
EXPECT_STREQ(TypeInfo<int>::name, "int");
EXPECT_STREQ(TypeInfo<double>::name, "double");
EXPECT_STREQ(TypeInfo<std::vector<int>>::name, "vector");
}
Integration Points
| Component | Interface |
|---|---|
stl-master |
Generic containers |
modern-cpp-expert |
Concepts, constexpr |
performance-optimizer |
Compile-time optimization |
cpp-algorithms-agent |
Generic algorithms |
C++ Plugin v3.0.0 - Production-Grade Development Skill