自从c++98开始,可以使用循环和路径选择执行的方式,在程序编译期执行计算。比如计算一个数是否为素数,可以使用如下的模板来实现:
template<unsigned p, unsigned d> struct DoIsPrime { static constexpr bool value = (p%d != 0) && DoIsPrime<p, d-1>::value; }; template<unsigned p> struct DoIsPrime<p,2> { static constexpr bool value = (p%2 != 0); }; template<unsigned p> struct IsPrime { static constexpr bool value = DoIsPrime<p,p/2>::value; }; //call the template function std::cout << IsPrime<9>::value << std::endl;
这里有两个问题:
- 为什么要声明为static?
- 是如何在编译期执行计算的?
先看问题1,声明为static是由于当实例化类时,类中的静态成员变量归类所有,所有对象共享统一份静态成员变量。换句话说,类特化时,不需要再定义对象即可使用该类中的静态数据。
再回答问题2,这里采用递归的方式展开计算。把展开过程梳理为:
IsPrime<9> ---> IsPrime<9,4>::value ---> 9%4 != 0 && DoIsPrime<9,3>::value ---> true && 9%3 !=0 && DoIsPrime<9,2>::value
在最后一步,由于9%3 != 0 是 false,根据经验,由前两项已经知道结果为false了,最后一个项根本不会计算,所以好像DoIsPrime<9,2>根本不会实例化。但是,这些是在编译期完成的!,因此还是会实例化。
当然还有比较清晰明了的普通函数式的写法
constexpr bool isPrime (unsigned int p) { for (unsigned int d = 2; d <= p/2; ++d){ if (p % d == 0){ return false; } } return p > 1; } //call the template function std::cout << isPrime(9) << std::endl;
从c++14开始,常量表达式在编译期完成计算,且支持循环等结构,不需要把所有语句在一行内完成。因此上面的函数也是在编译期完成的。