模板元编程入门
内容简介
模板元编程是利用模板在编译期计算类型和值。它不是普通业务代码的第一选择,但很多标准库工具、类型萃取、编译期优化和泛型框架都依赖它。
核心意义
普通程序在运行期计算:
int factorial(int n)
{
return n <= 1 ? 1 : n * factorial(n - 1);
}模板元编程可以在编译期计算:
template <int N>
struct Factorial {
static constexpr int value = N * Factorial<N - 1>::value;
};
template <>
struct Factorial<0> {
static constexpr int value = 1;
};
static_assert(Factorial<5>::value == 120);类型作为计算对象
模板元编程常常不是算数字,而是“根据类型产生类型”。
template <typename T>
struct AddPointer {
using type = T*;
};
using IntPtr = AddPointer<int>::type;现代 C++ 常用别名模板简化:
template <typename T>
using AddPointerT = T*;std::integral_constant
标准库用 std::integral_constant 表示编译期常量:
using TrueType = std::integral_constant<bool, true>;
using FalseType = std::integral_constant<bool, false>;std::true_type 和 std::false_type 就是它的常用别名。
类型萃取的基本形态
自己实现一个简化版 is_pointer:
template <typename T>
struct IsPointer : std::false_type {
};
template <typename T>
struct IsPointer<T*> : std::true_type {
};
static_assert(IsPointer<int*>::value);
static_assert(!IsPointer<int>::value);这就是“主模板 + 偏特化”的典型模式。
现代写法
很多传统模板元编程可以被更清楚的现代写法替代。
template <typename T>
void print(const T& value)
{
if constexpr (std::is_pointer_v<T>) {
if (value) {
std::cout << *value << '\n';
}
} else {
std::cout << value << '\n';
}
}最佳代码实践
- 普通业务代码不要为了炫技写模板元编程。
- 使用标准库类型萃取,不要重复造轮子。
- C++17 后优先考虑
if constexpr。 - C++20 后优先用 concepts 表达类型约束。
- 模板元编程代码要用
static_assert写清预期。
错误用法
template <typename T>
struct IsInt {
static const bool value = false;
};
// 忘记特化,所有类型都得到 false问题:模板元编程错误常发生在编译期,最好用 static_assert 立即验证。
注意事项
- 模板元编程是 C++ 最不透明的部分之一,应该按需学习。
- 理解类型萃取比背复杂技巧更重要。
- 高级主题包括 SFINAE、检测惯用法、concepts、折叠表达式、模板递归、非类型模板参数。