模板元编程 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 constexpr、type_traits、折叠表达式是现代 C++ TMP 的主要工具。避免过度使用 TMP,可读性同样重要。