那么我们第二个例子就是素数枚举
这个例子也有点小怪 当然也很有趣
这个模板是这么写的
template〈int p, int i〉 struct PrimeMagicCube
｛ enum ｛ answer = p % i && PrimeMagicCube〈p, i-1〉::answer ｝; ｝;
over 这个类模板干嘛用的呢
它就是计算 p 是不是素数的
答案就是它是不是素数
如果是素数 答案就是 true
否则就是 false
当然因为它是个枚举文字值
所以如果它是素数 answer 就是 1
如果它不是素数 answer 就是 0
就是这个意思
那我们怎么算它是不是素数呢
我们就按照它的素数的定义
取的是什么东西呢
取的就是看它能不能够被
小于 p 的所有的数整除
所以这个模板本身的意思就是看 p
能不能够被所有不大于 i 的值所整除
其实就是这个意思 不是被 i 所整除
而是被所有不大于 i 的值整除
为什么呢 因为能够被 i 整除这个结论
已经写在这个地方了
p % i 如果余数不为 0
结果就是 true
它不就把 1 传进去了 对吧
因为这是个逻辑运算嘛
它余数不管是几
只要不为 0 它就相当于 true
它就做了这个逻辑运算
得到的结果就是 true 和 false
最后又会转型成 1 和 0
然后传给 answer 嘛
它后面还跟着一个递归呢
PrimeMagicCube〈p, i-1〉
它就看 p % (i - 1) 能不能整除
对吧 那想看 p、i - 1 的时候
你不还要体化这个版本吗
体化这个类模板吗
OK 继续体化
可是到看 p、i - 1 的时候
那你不还要再看 p、i - 2 嘛
它就是按照这个模式
不断地向下递归的
看看能不能够被整除
对吧 结论就是这个
这个结构名字 我称它为素数魔方
专门做的就是这个事儿
我们使用的是 struct 关键字 没使用 class
所以这个东西自动就是 public
我少写了一行代码
要不我这个片子排不下
我们可以对这个素数魔方类
进行部分特化 以提供它递归终止版本
这里面是双参数
我只特化它的一个参数
那不就是部分特化吗
提供它的终止条件
对于 p 这个参数来讲
我们不需要特化它 只需要对 i
当 i 递减到 1 的时候
我其实就可以停了 素性就出来了
对不对 所以我们部分特化它
template〈int p〉 struct PrimeMagicCube〈p, 1〉｛ enum ｛ answer = 1 ｝; ｝;
true 一直递减到 1
都没有找到它的因子
都不能整除 answer 显然是 true
所以我们传 1 这个就是部分特化
现在我们想求给定值的素数
我们这里其实求的不是给定值
而是求不大于一个特定值的全部的素数
都打印出来
我们就定义一个类模板
template〈int i〉 struct Number
这就是一个数字模板
你传几它就是数字几
我们来看它是不是素数
有一个嵌套的对象在这里
Number〈i - 1〉 a
这是一个递归定义的数值对象
你想定义数值 n
那么在数值 n 里边就有一个成员
是数值 n - 1 的一个对象
在数值 n - 1 的那个里边
就有一个数值 n - 2 的一个对象
它是按照这个方式递归定义的
为什么呢 就是因为我们就要用它
来递归地去求所有的素数
当然它还有一个
enum｛ answer = PrimeMagicCube〈i, i-1〉::answer ｝
就去求这个模板 PrimeMagicCube〈i, i-1〉
看它的 answer 是几
就初始化给我们这个 answer
什么意思 你给我传一个数 i
你问我 i 是不是素数
那我们就从 i - 1 开始除起
看 i - 1 能不能整除 不能
i - 2 能不能整除 不能
i - 3 能不能整除 不能
一直到除到 1（注：不包含 1 本身） 都不能
编译期就能算出来 answer 就是 1
在任意一个时刻 发现它能够整除
那么这个结论就会变成 0
那个 0 和任何量 “&&”
它结果不都是 0 嘛
对吧 都是 false 嘛
所以结果传 0 就进去了
我们提供一个函数 IsPrime()
问你是不是素数
我们实际上会把这个素数给打印出来
if( answer )
如果这个数值对象 你传的这个 i
这个数是个素数
那么 answer 它就会写 1 在那里边
那么我们就会输出这个素数
如果不是 我就不输出它
就是合数 我就不输出了
然后我递归调用 a.IsPrime()
为啥要递归调用呢
因为我要看 i - 1 那个数是不是素数
不是说了嘛
我要输出不大于 i 的全部素数
a.IsPrime() 它就会递归调用
去看 i - 1 是不是素数
如果是素数 它就把它打印出来
然后递归调用
怎么又递归调用了
你是在 a 这个对象上面调用的 IsPrime() 呀
不是 Number〈i〉 的那个对象
是 Number〈i - 1〉 的那个对象
你在 IsPrime() 里边调用了这个 a.IsPrime()
你调用的是 Number〈i - 1〉 的那个对象 a
在它上调用 IsPrime()
它就会执行 它打印 i - 1
它是不是素性的那个 answer
然后那个对象本身
不还有一个成员名字叫 a 嘛
你又在 Number〈i - 1〉 的那个成员 a
那个成员 a 是什么 是 Number〈i - 2〉 呀
又在那个上面调用了IsPrime()
它就把 i - 2 那个是不是素数的结论
它不又输出了嘛
它就按照这个方式
递归地输出了全部的素数
从大到小 你传我 100
第一个输出的就是 97
最后输出的才是 2 按这个模式输出
最后我再写一个输出我们的 answer 的值
我们的 answer 是什么呢
answer 不是 0 就是 1
最后不管三七二十一
我都把它的 1 或者 0 的值输出出来
你注意我这个递归调用
因为这个 a.IsPrime() 的递归调用
是写在 if( answer ) 的后边
写在后边一条输出语句的前边
所以全部素数的输出是从大到小排列的
但是从 2 开始的所有数的素性
是从小到大输出的
所以你看到
它最后的输出的结果那个序列
它是什么呢
1、1、0、1、0、1...
为什么是这个序列
就是 2 是素数 所以输出 1
3 是素数 所以输出 1
4 是合数 所以输出 0
5 是素数呀 所以输出 1
6 是合数 所以输出 0
7 是素数 所以输出 1...
就是这个格式 1、1、0、1、0、1...
按照这个格式
后面的当然是 0、0、0、1
对吧 你就会看到所有的这个素性
用一个 0、1 序列把它输出出来
如果你的程序可以处理这个 0、1 序列
这个素性
一个不大于 n 的这个值的全部的素性
你一下子就能够全得到
而这都是编译期做到的
我们的 Number 这个数值类
同样需要提供一个特化的版本
我们要特化 让它终止于 2
所以如果这个数到了素数 2
那么 answer 显然就是 1
输出的时候呢 就直接输出 2 的值
然后立即就开始输出它的 answer
就是递归到此就开始发生了变化
它就停止
我们的实现 我们 Number〈100〉 a
就是输出不大于 100 的全部的素数和它的素性的列表
素数从大到小输出
素性列表 0、1 序列从小到大输出
当然终止只到 2
我们不看 1 的 就是 2 到 100 之间的
全部的素数给列出来了
2 到 100 的所有的数的素性
0、1 序列也被我们列出来了
这就是素数枚举这个实现
给我们提供的一个解决方案
看上去非常非常有趣 很神奇
就能在编译期完成全部的
素数的计算和枚举