元程序是在编译期由编译器直接解析并执行的。
在编译期,编译器只能做整数值计算和类型计算,这就导致了元程序的代码结构和我们熟知的代码结构(运行时代码结构)有很大区别。
- 编译期计算:所有计算在编译时完成,运行时无额外开销。
- 递归实现逻辑:由于模板缺乏变量和循环,依赖递归和模板特化进行计算。
- 类型萃取:可用于类型推导、类型转换及静态检查。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| template<int N> struct Fibonacci { static constexpr int num = Fibonacci<N - 1>::num + Fibonacci<N - 2>::num; };
template<> struct Fibonacci<0> { static constexpr int num = 0; };
template<> struct Fibonacci<1> { static constexpr int num = 1; };
|
查看汇编,可以看到值已经求出来:
image20250316131356284.png
这是一道利用模板元编程编译期计算出斐波那契数的例子,解读如下:
- 在模板元编程中,我们通常使用
struct
而不是
class
。但是并没有硬性规定不能用
class
,只要手动加上 public
,class
也能实现相同的功能
- 变量是静态的(static),方便后面能够通过
::
访问到,如:Fibonacci<N - 1>::num
- 得加上 constexpr 或 const 属性才能让你编译期间计算,但建议统一使用
constexpr
- 由于模板缺乏变量和循环,依赖递归和模板特化进行计算。既然是递归,那就得有终止条件,因此得全特化原模板,实现终止条件
我们也可以正常写 斐波那契函数,然后用 constexpr
修饰函数返回值,达到编译期计算的目的:
1 2 3 4 5
| constexpr int fn(int N) { if (N == 0) return 0; if (N == 1) return 1; return fn(N - 1) + fn(N - 2); }
|
但如果你是在 C++11 这样写就会报错(C++11 constexpr 不允许递归),但是
C++14 往上没有问题,随着版本的提高,constexpr 的能力或者自由会越多。
image20250316133054394.png
C++17 将 constexpr 这个关键字引入到 if
语句中,允许在代码中声明常量表达式的判断条件:
1 2 3 4 5 6 7 8 9 10 11
| #include <iostream> #include <type_traits>
template<typename T> auto print_type_info(const T& t) { if constexpr (std::is_integral<T>::value) { return t + 1; } else { return t + 0.001; } }
|
如果你按照下面的方式调用:
1 2 3 4
| int main() { std::cout << print_type_info(5) << std::endl; std::cout << print_type_info(3.14) << std::endl; }
|
编译期间,代码表现为:
1 2 3 4 5 6 7 8 9 10
| int print_type_info(const int& t) { return t + 1; } double print_type_info(const double& t) { return t + 0.001; } int main() { std::cout << print_type_info(5) << std::endl; std::cout << print_type_info(3.14) << std::endl; }
|
if constexpr
是 C++17 新增的
编译期分支判断,其主要作用是:
- 在编译期决定是否编译某个代码分支。
- 不会编译无效的分支,避免编译错误。
如果所有分支都走不通的话,就会报错,不会编译通过。
constexpr
结合
std::array
(编译期缓存):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| constexpr std::array<int, 11> fibonacci_table = [] { std::array<int, 11> arr{}; arr[0] = 0; arr[1] = 1; for (int i = 2; i < 11; ++i) { arr[i] = arr[i - 1] + arr[i - 2]; } return arr; }();
int main() { constexpr int num = fibonacci_table[10]; std::cout << num << std::endl; return 0; }
|
参考内容
C++模板元编程