C++templates第八章编译期编程
C++templates第八章编译期编程
Hoshea ZhangC++一直以来都包含一些可以被用来进行编译器计算的简单方法。模板则进一步增加了编译器计算的可能性,而且该语言进一步的发展通常也都是在这一工具箱里进行的。
比较简单的情况是,可以通过它来决定是否启用某个模板,或者在多个模板之间做选择。不 过如果有足够多的信息,编译器甚至可以计算控制流的结果。
模板元编程
模板的实例化发生在编译期间(而动态语言的泛型是在程序运行期间决定的)。事实证明 C++模板的某些特性可以和实例化过程相结合,这样就产生了一种 C++自己内部的原始递归 的“编程语言”。因此模板可以用来“计算一个程序的结果”。
下面的代码在编译期间就能判断一个数是不是质数:
1 | template<unsigned p, unsigned d> // p: number to check, d: current divisor |
IsPrime<>模板将结果存储在其成员 value 中。为了计算出模板参数是不是质数,它实例化了 DoIsPrime<>模板,这个模板会被递归展开,以计算 p 除以 p/2 和 2 之间的数之后是否会有余数。
通过constexpr进行计算
C++11 引入了一个叫做 constexpr 的新特性,它大大简化了各种类型的编译期计算。如果给 定了合适的输入, constexpr 函数就可以在编译期间完成相应的计算。虽然 C++11 对 constexpr 函数的使用有诸多限制(比如 constexpt 函数的定义通常都只能包含一个 return 语句),但 是在 C++14 中这些限制中的大部分都被移除了。
在 C++14 中, constexpr 函数可以使用常规 C++代码中大部分的控制结构。因此为了判断一个 数是不是质数,可以不再使用笨拙的模板方式(C++11 之前)以及略显神秘的单行代码方式 (C++11),而直接使用一个简单的 for 循环:
1 | constexpr bool isPrime (unsigned int p) |
但是上面所说的“可以”在编译期执行,并不是一定会在编译期执行。在需要编译期数值的上下文中(比如数组的长度和非类型模板参数),编译器会尝试在编译期对被调用的 constexpr 函数进行计算,此时如果无法在编译期进行计算,就会报错 (因为此处必须要产生一个常量)。在其他上下文中,编译期可能会也可能不会尝试进行编译期计算,如果在编译期尝试了,但是现有条件不满足编译期计算的要求,那么也不会报错, 相应的函数调用被推迟到运行期间执行
编译器if
通过使用 ifconstexpr(…)语法,编译器会使用编译期表达式来决定是使用 if 语句的 then 对应的部分还是 else 对应的部分
作为第一个例子,考虑 4.1.1 节介绍的变参函数模板 print()。它用递归的方法打印其参数(可能是任意类型)。如果使用 constexpif,就可以在函数内部决定是否要继续递归下去,而不用再单独定义一个函数来终结递归:
1 | template<typename T, typename… Types> |