C++模板元编程Skill cpp-templates-metaprogramming

这个技能涉及C++模板和元编程,包括函数模板、类模板、特化、SFINAE、类型特征、变参模板、编译时计算和C++20概念。它用于实现泛型编程、编译时优化、高效代码生成和类型安全接口,适用于高性能库开发、系统编程和架构设计。关键词:C++模板、元编程、编译时计算、泛型编程、SFINAE、类型特征、C++20概念、模板特化、变参模板。

架构设计 0 次安装 0 次浏览 更新于 3/25/2026

name: cpp-templates-metaprogramming user-invocable: false description: 当需要C++模板和元编程时使用,包括模板特化、SFINAE、类型特征和C++20概念。 allowed-tools:

  • 读取
  • 写入
  • 编辑
  • 搜索
  • 全局
  • Bash

C++ 模板和元编程

模板元编程允许编译时计算和代码生成,创建灵活、高效的抽象而无需运行时开销。此技能涵盖函数和类模板、特化、SFINAE、类型特征和现代基于概念的模板约束。

函数模板

函数模板允许编写适用于任何满足要求的类型的通用算法。

#include <iostream>
#include <vector>
#include <string>

// 基本函数模板
template<typename T>
T maximum(T a, T b) {
    return (a > b) ? a : b;
}

// 多个模板参数
template<typename T, typename U>
auto add(T a, U b) -> decltype(a + b) {
    return a + b;
}

// 带有非类型参数的模板
template<typename T, size_t N>
size_t array_size(T (&)[N]) {
    return N;
}

// 模板重载
template<typename T>
void print(T value) {
    std::cout << value << "
";
}

template<typename T>
void print(const std::vector<T>& vec) {
    for (const auto& item : vec) {
        std::cout << item << " ";
    }
    std::cout << "
";
}

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(arr) << "
";

    print(42);
    print(std::vector<int>{1, 2, 3});
}

类模板

类模板允许创建通用容器和数据结构。

#include <iostream>
#include <stdexcept>

// 基本类模板
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("堆栈为空");
        }
        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;
    }
};

// 多个模板参数
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<typename T, typename Allocator = std::allocator<T>>
class Vector {
    // 实现
};

void class_template_examples() {
    Stack<int> int_stack;
    int_stack.push(1);
    int_stack.push(2);
    std::cout << int_stack.pop() << "
";

    Stack<std::string> str_stack;
    str_stack.push("hello");

    Pair<std::string, int> p("age", 30);
}

模板特化

模板特化允许为特定类型提供自定义实现。

#include <iostream>
#include <cstring>

// 主模板
template<typename T>
class Container {
    T value_;

public:
    Container(const T& value) : value_(value) {}

    void print() const {
        std::cout << "通用: " << value_ << "
";
    }

    size_t memory_size() const {
        return sizeof(T);
    }
};

// 对const char*的完全特化
template<>
class Container<const char*> {
    const char* value_;

public:
    Container(const char* value) : value_(value) {}

    void print() const {
        std::cout << "C字符串: " << value_ << "
";
    }

    size_t memory_size() const {
        return std::strlen(value_) + 1;
    }
};

// 对指针的部分特化
template<typename T>
class Container<T*> {
    T* value_;

public:
    Container(T* value) : value_(value) {}

    void print() const {
        std::cout << "指针: " << *value_ << "
";
    }

    size_t memory_size() const {
        return sizeof(T*);
    }
};

// 函数模板特化
template<typename T>
bool is_negative(T value) {
    return value < 0;
}

template<>
bool is_negative<bool>(bool value) {
    return false;  // 布尔值不能为负
}

void specialization_examples() {
    Container<int> c1(42);
    c1.print();  // 通用

    Container<const char*> c2("hello");
    c2.print();  // C字符串

    int x = 10;
    Container<int*> c3(&x);
    c3.print();  // 指针
}

SFINAE(替换失败不是错误)

SFINAE允许基于类型属性的编译时函数选择。

#include <iostream>
#include <type_traits>
#include <vector>

// 启用如果类型有begin()和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 << "容器: ";
    for (const auto& item : container) {
        std::cout << item << " ";
    }
    std::cout << "
";
}

// 启用如果类型是算术类型
template<typename T>
typename std::enable_if<std::is_arithmetic<T>::value>::type
print_value(T value) {
    std::cout << "数字: " << value << "
";
}

// 启用如果类型不是算术类型
template<typename T>
typename std::enable_if<!std::is_arithmetic<T>::value>::type
print_value(const T& value) {
    std::cout << "非数字: " << value << "
";
}

// 使用std::enable_if作为模板参数
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("除以零");
    }
    return a / b;
}

// 标签分发(SFINAE的替代方法)
template<typename T>
void process_impl(T value, std::true_type /* is_pointer */) {
    std::cout << "处理指针: " << *value << "
";
}

template<typename T>
void process_impl(T value, std::false_type /* is_pointer */) {
    std::cout << "处理值: " << value << "
";
}

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) << "
";

    int x = 100;
    process(x);
    process(&x);
}

类型特征

类型特征提供编译时类型信息和转换。

#include <type_traits>
#include <iostream>
#include <string>

// 使用标准类型特征
template<typename T>
void analyze_type() {
    std::cout << "类型分析:
";
    std::cout << "  是否是整数: "
              << std::is_integral<T>::value << "
";
    std::cout << "  是否是浮点数: "
              << std::is_floating_point<T>::value << "
";
    std::cout << "  是否是指针: "
              << std::is_pointer<T>::value << "
";
    std::cout << "  是否是const: "
              << std::is_const<T>::value << "
";
    std::cout << "  大小: " << sizeof(T) << "
";
}

// 类型转换
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 << "移除cv后是否相同: "
              << std::is_same<NoCV, T>::value << "
";
}

// 自定义类型特征
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;

// 条件类型
template<typename T>
using MakeUnsigned = std::conditional_t<
    std::is_signed<T>::value,
    std::make_unsigned_t<T>,
    T
>;

// 编译时if(C++17)
template<typename T>
void print_type(const T& value) {
    if constexpr (std::is_integral_v<T>) {
        std::cout << "整数: " << value << "
";
    } else if constexpr (std::is_floating_point_v<T>) {
        std::cout << "浮点数: " << value << "
";
    } else if constexpr (is_string_v<T>) {
        std::cout << "字符串: " << value << "
";
    } else {
        std::cout << "未知类型
";
    }
}

void type_traits_examples() {
    analyze_type<int>();
    analyze_type<const double*>();

    print_type(42);
    print_type(3.14);
    print_type(std::string("hello"));
}

变参模板

变参模板允许函数和类接受任意数量的参数。

#include <iostream>
#include <sstream>

// 基本情况
void print_all() {
    std::cout << "
";
}

// 递归变参模板
template<typename T, typename... Args>
void print_all(T first, Args... rest) {
    std::cout << first << " ";
    print_all(rest...);
}

// 折叠表达式(C++17)
template<typename... Args>
auto sum_all(Args... args) {
    return (args + ...);
}

template<typename... Args>
auto multiply_all(Args... args) {
    return (args * ... * 1);
}

// 变参类模板
template<typename... Types>
class Tuple;

template<>
class Tuple<> {
public:
    static constexpr size_t size = 0;
};

template<typename Head, typename... Tail>
class Tuple<Head, Tail...> : private Tuple<Tail...> {
    Head head_;

public:
    static constexpr size_t size = 1 + Tuple<Tail...>::size;

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

    Head& head() { return head_; }
    const Head& head() const { return head_; }

    Tuple<Tail...>& tail() {
        return *this;
    }
};

// 参数包扩展
template<typename... Args>
void process_all(Args... args) {
    // 在初始化列表中扩展
    int dummy[] = { (std::cout << args << " ", 0)... };
    (void)dummy;  // 抑制未使用警告
}

// 用于编译时迭代的索引序列
template<size_t... Is>
void print_indices(std::index_sequence<Is...>) {
    ((std::cout << Is << " "), ...);
    std::cout << "
";
}

void variadic_examples() {
    print_all(1, 2.5, "hello", std::string("world"));

    auto total = sum_all(1, 2, 3, 4, 5);
    auto product = multiply_all(2, 3, 4);

    Tuple<int, double, std::string> t(42, 3.14, "test");
    std::cout << "元组大小: " << decltype(t)::size << "
";

    print_indices(std::make_index_sequence<5>{});
}

模板元编程

模板元编程使用模板执行编译时计算。

#include <iostream>

// 编译时阶乘
template<int N>
struct Factorial {
    static constexpr int value = N * Factorial<N - 1>::value;
};

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

// 编译时斐波那契数列
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;
};

// 类型列表操作
template<typename... Types>
struct TypeList {};

// 获取类型列表大小
template<typename List>
struct Length;

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

// 获取索引处的元素
template<size_t Index, typename List>
struct At;

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

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

// 检查类型是否在列表中
template<typename T, typename List>
struct Contains;

template<typename T>
struct Contains<T, TypeList<>> : std::false_type {};

template<typename T, typename Head, typename... Tail>
struct Contains<T, TypeList<Head, Tail...>>
    : Contains<T, TypeList<Tail...>> {};

template<typename T, typename... Tail>
struct Contains<T, TypeList<T, Tail...>> : std::true_type {};

// Constexpr函数(C++11及以后)
constexpr int factorial_constexpr(int n) {
    return (n <= 1) ? 1 : n * factorial_constexpr(n - 1);
}

constexpr int fibonacci_constexpr(int n) {
    return (n <= 1) ? n : fibonacci_constexpr(n - 1) +
                          fibonacci_constexpr(n - 2);
}

void metaprogramming_examples() {
    // 在编译时计算
    constexpr int fact5 = Factorial<5>::value;
    constexpr int fib7 = Fibonacci<7>::value;

    std::cout << "5! = " << fact5 << "
";
    std::cout << "fib(7) = " << fib7 << "
";

    using MyTypes = TypeList<int, double, std::string>;
    std::cout << "类型列表长度: "
              << Length<MyTypes>::value << "
";

    using SecondType = At<1, MyTypes>::type;  // double
    std::cout << "包含int: "
              << Contains<int, MyTypes>::value << "
";

    // 现代constexpr
    constexpr int fact6 = factorial_constexpr(6);
    std::cout << "6! = " << fact6 << "
";
}

概念(C++20)

概念为模板参数提供命名约束,具有更好的错误消息。

#include <concepts>
#include <iostream>

// 基本概念
template<typename T>
concept Numeric = std::integral<T> || std::floating_point<T>;

// 带有要求的概念
template<typename T>
concept Addable = requires(T a, T b) {
    { a + b } -> std::convertible_to<T>;
};

// 带有多个约束的概念
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>;
};

// 在函数模板中使用概念
template<Numeric T>
T add(T a, T b) {
    return a + b;
}

// 概念作为返回类型约束
template<typename T>
auto square(T x) -> std::same_as<T> auto {
    return x * x;
}

// 多个概念约束
template<typename T>
concept Sortable = std::totally_ordered<T> && std::copyable<T>;

template<Sortable T>
void sort_values(std::vector<T>& values) {
    std::sort(values.begin(), values.end());
}

// 包含(概念细化)
template<typename T>
concept SignedNumeric = Numeric<T> && std::signed_integral<T>;

template<Numeric T>
void process(T value) {
    std::cout << "处理数值
";
}

template<SignedNumeric T>
void process(T value) {
    std::cout << "处理有符号数值
";
}

void concepts_examples() {
    auto result = add(5, 10);      // 正确
    auto dresult = add(5.5, 2.3);  // 正确
    // auto sresult = add("hi", "there");  // 错误

    std::vector<int> vec{3, 1, 2};
    sort_values(vec);

    process(5);   // 调用有符号数值版本
    process(5.5); // 调用数值版本
}

最佳实践

  1. 在C++20中,使用概念代替SFINAE以获得更清晰的模板约束
  2. 为了提高可读性,优先使用constexpr函数而不是模板元编程
  3. 使用std::enable_if_t和类型特征的_v_t后缀以简化代码
  4. 即使没有概念,也要清晰地记录模板要求
  5. 使用decltype(auto)进行完美的返回类型推断
  6. 当完全实现不同时,优先使用模板特化而不是SFINAE
  7. 在可能时,使用折叠表达式代替递归变参模板
  8. 标记模板函数为inline或在头文件中定义以避免链接错误
  9. 使用static_assert在编译时验证模板参数
  10. 优先使用标准库类型特征而不是自定义实现

常见陷阱

  1. 忘记在头文件中定义模板成员函数,导致链接错误
  2. 没有适当基类的无限模板递归
  3. 难以阅读和维护的复杂SFINAE表达式
  4. 在引用依赖类型时没有使用typename关键字
  5. 由于大型模板的不必要实例化导致模板膨胀
  6. 模板特化中的循环依赖
  7. 当多个SFINAE条件匹配时,函数重载不明确
  8. 复杂模板元编程导致的过度编译时间
  9. 没有将模板constexpr函数标记为constexpr
  10. 当运行时多态更简单且足够时使用模板

何时使用模板和元编程

在需要时使用模板和元编程:

  • 适用于多种类型的通用算法
  • 编译时计算和代码生成
  • 无运行时成本的零开销抽象
  • 具有强编译时检查的类型安全接口
  • 适用于任何类型的容器和数据结构
  • 用于领域特定语言的表达式模板
  • 具有编译时配置的策略驱动设计
  • 消除相似实现之间的代码重复
  • 无虚函数开销的静态多态性
  • 现代C++库中灵活、可组合的组件

资源