Skip to content

模板元编程 TMP

模板元编程让 C++ 在编译期执行计算,实现零运行时开销的泛型抽象。这是 C++ 区别于其他语言的核心能力。

函数模板

cpp
#include <type_traits>

// 基础函数模板
template<typename T>
T max_val(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;
}

// C++14:auto 返回类型推导
template<typename T, typename U>
auto multiply(T a, U b) {
    return a * b;
}

// 模板特化
template<>
const char* max_val(const char* a, const char* b) {
    return strcmp(a, b) > 0 ? a : b;
}

// 非类型模板参数
template<int N>
int factorial() {
    return N * factorial<N - 1>();
}
template<>
int factorial<0>() { return 1; }

// 编译期计算
static_assert(factorial<5>() == 120);

类模板

cpp
// 泛型容器
template<typename T, size_t N>
class Array {
    T data_[N];

public:
    T& operator[](size_t i) { return data_[i]; }
    const T& operator[](size_t i) const { return data_[i]; }
    constexpr size_t size() const { return N; }

    T* begin() { return data_; }
    T* end()   { return data_ + N; }
};

Array<int, 5> arr;
arr[0] = 42;
for (auto x : arr) { /* ... */ }

// 模板偏特化
template<typename T>
class Array<T, 0> {  // 大小为 0 的特化
    // 空实现
};

// 指针特化
template<typename T>
class SmartRef<T*> {
    // 针对指针类型的特殊处理
};

类型萃取(Type Traits)

cpp
#include <type_traits>

// 标准库提供的类型萃取
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_same_v<int, int>);
static_assert(!std::is_same_v<int, long>);

// 类型变换
using T1 = std::remove_const_t<const int>;    // int
using T2 = std::add_pointer_t<int>;           // int*
using T3 = std::remove_reference_t<int&>;     // int
using T4 = std::decay_t<const int&>;          // int

// 自定义类型萃取
template<typename T>
struct is_string : std::false_type {};

template<>
struct is_string<std::string> : std::true_type {};

template<>
struct is_string<std::string_view> : std::true_type {};

template<typename T>
constexpr bool is_string_v = is_string<T>::value;

static_assert(is_string_v<std::string>);
static_assert(!is_string_v<int>);

SFINAE(替换失败不是错误)

cpp
#include <type_traits>

// enable_if:条件启用函数
template<typename T>
std::enable_if_t<std::is_integral_v<T>, T>
double_it(T x) {
    return x * 2;
}

// 只对整数类型有效
double_it(5);    // OK
// double_it(3.14); // 编译错误,没有匹配的重载

// 更清晰的写法(C++17 if constexpr)
template<typename T>
auto process(T x) {
    if constexpr (std::is_integral_v<T>) {
        return x * 2;
    } else if constexpr (std::is_floating_point_v<T>) {
        return x * 2.0;
    } else {
        static_assert(std::is_arithmetic_v<T>, "需要数值类型");
    }
}

// void_t 技巧:检测类型是否有某个成员
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 {};

static_assert(has_size<std::vector<int>>::value);
static_assert(!has_size<int>::value);

可变参数模板(Variadic Templates)

cpp
#include <iostream>
#include <tuple>

// 递归展开
template<typename T>
void print(T&& t) {
    std::cout << t << "\n";
}

template<typename T, typename... Args>
void print(T&& first, Args&&... rest) {
    std::cout << first << " ";
    print(std::forward<Args>(rest)...);  // 递归
}

print(1, "hello", 3.14, true);  // 1 hello 3.14 1

// C++17 折叠表达式(更简洁)
template<typename... Args>
void print_fold(Args&&... args) {
    ((std::cout << args << " "), ...);  // 折叠表达式
    std::cout << "\n";
}

// 求和
template<typename... Args>
auto sum(Args... args) {
    return (args + ...);  // 一元右折叠
}

static_assert(sum(1, 2, 3, 4, 5) == 15);

// 完美转发所有参数
template<typename F, typename... Args>
auto invoke(F&& f, Args&&... args) {
    return std::forward<F>(f)(std::forward<Args>(args)...);
}

编译期计算

cpp
// constexpr 函数(C++11/14/17/20 逐步增强)
constexpr int fibonacci(int n) {
    if (n <= 1) return n;
    return fibonacci(n - 1) + fibonacci(n - 2);
}

static_assert(fibonacci(10) == 55);  // 编译期计算

// 编译期字符串
constexpr size_t strlen_ct(const char* s) {
    size_t len = 0;
    while (s[len]) ++len;
    return len;
}

static_assert(strlen_ct("hello") == 5);

// 编译期排序(C++20)
#include <array>
#include <algorithm>

constexpr auto sorted = [] {
    std::array<int, 5> arr{5, 3, 1, 4, 2};
    std::sort(arr.begin(), arr.end());
    return arr;
}();

static_assert(sorted[0] == 1);
static_assert(sorted[4] == 5);

策略模式(Policy-Based Design)

cpp
// 通过模板参数注入策略,零运行时开销
template<
    typename StoragePolicy,
    typename LoggingPolicy,
    typename LockingPolicy
>
class DataManager : private StoragePolicy
                  , private LoggingPolicy
                  , private LockingPolicy
{
public:
    void save(const Data& d) {
        LockingPolicy::lock();
        LoggingPolicy::log("saving...");
        StoragePolicy::write(d);
        LockingPolicy::unlock();
    }
};

// 策略实现
struct FileStorage {
    void write(const Data& d) { /* 写文件 */ }
};

struct NoLogging {
    void log(const char*) {}  // 空操作,编译器会优化掉
};

struct SingleThreaded {
    void lock() {}
    void unlock() {}
};

// 组合策略
using FastManager = DataManager<FileStorage, NoLogging, SingleThreaded>;

关键认知

模板元编程的核心价值是将运行时开销转移到编译期if constexprtype_traits、折叠表达式是现代 C++ TMP 的主要工具。避免过度使用 TMP,可读性同样重要。

系统学习 C++ 生态,深入底层架构