模板元编程(英语:Template metaprogramming;缩写:TMP)是一种元编程技术,不夸张的说,这项技术开启了一种新的C++编程方式。编译器使用模板产生暂时性的源码,然后再和剩下的源码混合并编译。这些模板的输出包括编译时期常数、数据结构以及完整的函数。如此利用模板可以被想成编译期的运行。本文介绍了利用模板元编程技术实现在编译期判断一个整数是否为素数的算法。输入为一个大于0的整数,输出为1表示该整数为素数,为0表示为合数。本文的主要目的是用实例说明模板元编程实现算术和逻辑运算的一般设计和编写方法。模板元编程的概念和基本介绍参见维基百科:http://zh.wikipedia.org/wiki/模板超編程
我们使用最基本的素数判断算法,伪代码如下:
function IsPrime(n) if n == 1 then return false if n == 2 then return true for each m from 2 if m * m > n then return true if n mod m = 0 then return false m := m + 1
这显然是一个复杂度为O(sqrt(n))的算法,主要的逻辑为循环。模板元编程是以递归的逻辑形式来实现循环算法,因此必须先明确两件事:有几个变量参与,以及循环的终止条件是什么。这个算法显然有2个变量参与运算:一个是n,另一个是m。我们令m从2开始递增,直到达到循环终止条件。在模板元编程中,由于模板参数推导的优先级是以特化程度排列的,因此终止条件和特殊值的处理使用偏特化(也叫部分特化)实现。关于模板参数推导和偏特化的概念和语法这里不做赘述,请参阅C++书籍或搜索网页资料。那么根据以上分析,我们可以先写出一个框架如下:
template<uint n, uint m>struct TEST{ const static uint r = TEST<n, nextM>::r; //nextM为下一个M,暂不实现。在这里用递归结构代替了循环 }; template<uint n>struct ISPRIME{ const static uint r = TEST<n, 2>::r; //从2开始,依次判断每一个可能的m取值,判断代码暂未实现。 }; template<>struct ISPRIME<1>{ //对于算法不能计算的特殊值1,判断为0 const static uint r = 0; }; template<>struct ISPRIME<2>{ //对于算法不能计算的特殊值2,判断为1 const static uint r = 1; };
循环的终止条件是:m的平方大于n或可以整除n。当满足终止条件时,向模板参数传递一个特殊值,并在偏特化中处理这个值,那么递归逻辑就终止了。而判断是否满足终止条件,则需进行逻辑和算术运算。基于以上分析,框架代码改写如下:
template<uint n, uint m>struct TEST{ // if (n % m == 0) n = 0; // if (m * m > n) m = 0; else ++m; const static uint r = TEST<n, m>::r; //上面两行代码不能写在此处,仅说明逻辑。实际的语法下文再做介绍 }; template<uint m>struct TEST<0, m>{ //n为0的情况 const static uint r = 0; //即在非特化的模板代码中,n可以被m整除,因此n被赋值为0,故为合数 }; template<uint n>struct TEST<n, 0>{ //m为0的情况 const static uint r = 1; //即在非特化的模板代码中,m * m > n,因此n不能被任何比它小的数整除,故为素数 }; template<uint n>struct ISPRIME{ const static uint r = TEST<n, 2>::r; //从2开始,依次判断每一个可能的m取值,判断代码暂未实现。 }; template<>struct ISPRIME<1>{ //对于算法不能计算的特殊值1,判断为0 const static uint r = 0; }; template<>struct ISPRIME<2>{ //对于算法不能计算的特殊值2,判断为1 const static uint r = 1; };
最后只要用模板的参数推导实现取模的算术运算和上面框架中的两个逻辑判断即可,完整代码如下:
#include <iostream> typedef unsigned int uint; template<uint n, uint m>struct NEXTN{ const static uint r = ((n % m != 0) * n); }; template<uint n, uint m>struct NEXTM{ const static uint r = (m * m <= n ? (m + 1) : 0); }; template<uint n, uint m>struct TEST{ const static uint r = TEST<NEXTN<n, m>::r, NEXTM<n, m>::r>::r; }; template<uint m>struct TEST<0, m>{ const static uint r = 0; }; template<uint n>struct TEST<n, 0>{ const static uint r = 1; }; template<uint n>struct ISPRIME{ const static uint r = TEST<n, 2>::r; }; template<>struct ISPRIME<1>{ const static uint r = 0; }; template<>struct ISPRIME<2>{ const static uint r = 1; }; int main() { int primes[] = { ISPRIME<1>::r, ISPRIME<2>::r, ISPRIME<3>::r, ISPRIME<4>::r, ISPRIME<5>::r, ISPRIME<6>::r, ISPRIME<7>::r, ISPRIME<8>::r, ISPRIME<9>::r, ISPRIME<10>::r, ISPRIME<11>::r, ISPRIME<12>::r, ISPRIME<13>::r, ISPRIME<14>::r, ISPRIME<15>::r, ISPRIME<16>::r, }; for (int i = 0; i < sizeof(primes) / sizeof(primes[0]); ++i) std::cout << i + 1 << (primes[i] ? " YES" : " NO") <<std::endl; return 0; }
如果您有更简洁的写法,请回复告知。谢谢!
作者:王雨濛;新浪微博:@吉祥村码农;来源:《程序控》博客 -- http://www.cnblogs.com/devymex/ 此文章版权归作者所有(有特别声明的除外),转载必须注明作者及来源。您不能用于商业目的也不能修改原文内容。 |