一:问题
一个问题:打印250(包括250)以内的所有素数;
第一种方法:自己找出所有的素数,并放入一个数组中,可能的一种代码如下:
#include <iostream>
int main()
{
int primes[] = { 2, 3, 5, 7, 9, 11, 13 .......... };//需要自己找,自己算,猝死.......
//打印
for(int i = 0; i < sizeof(primes); ++i)
std::cout << "第" + << i + 1 << "个素数是: " << primes[i] << std::endl;
return 0;
}
第二种方法:让程序帮我们找,我们可以写一个判断是不是素数的函数,然后再写一个循环,一种可能的代码如下
#include <iostream>
bool isPrime(int i)
{
/*
* 具体实现,只需判断sqrt(i)以内有没有是i的因子,具体实现就不写了
*/
...
}
#define N 250
int main()
{
//记录总共多少个
int count = 0;
for(int i = 2; i <= N; ++i)
{
if(isPrime(i))
{
++count;
//打印
std::cout << "第" + << count << "个素数是: " << i << std::endl;
}
}
return 0;
}
现在我们看一下这两种方法,第一种应该没有人愿意那样写吧,所以直接看第二种;
在第二种方法中,程序运行之后,会帮我们一个一个的判断哪些是素数,如果当前这个数是素数,那就打印出来;
第二种方法是没有任何问题的,但250以内的素数有几个,是哪些,不管如何这些一定是确定下来的,只是我们懒得计算,将这个任务交给了程序,让程序帮我们计算,这样势必会让程序做一些不必要的计算,我们希望一个程序只计算它必须要计算的;
那么有没有一个折中的方法,我们可以多敲一些代码,但同时又不需要我们自己找、自己算;答案肯定是有,那就是模板元。
二:模板元
模板元说到底就是让编译器帮我们算好,然后程序就可以直接使用(本来我们可以自己算出来的,就像第一种方法一样)
首先来一个最简单最常见的:模板元计算n!(n的阶乘)
一种可能的代码如下:
#include <iostream>
template <int N>
struct Fac
{
enum { value = N * Fac<N-1>::value };
};
template <>
struct Fac<1>
{
enum { value = 1 };
};
int main()
{
std::cout << "10的阶乘 : " << Fac<10>::value; // 10的阶乘 : 3628800
return 0;
}
在上面的程序中,编译器会帮我们计算出10!,当然如果我们要自己算的话,你需要准备一根笔,一本草稿本,然后按照阶乘的定义一步一步算;
然后回到正文,怎么找250以内的所有素数呢?
废话不多说,直接上源码;
#include <iostream>
#include <vector>
//模板元选择结构
template <bool isPrime, typename Then, typename Else>
struct If
{
typedef Then Ret;
};
template <typename Then, typename Else>
struct If<false, Then, Else>
{
typedef Else Ret;
};
//
template <bool bValue>
struct Bool
{
const static bool value = bValue;
};
//辅助IsPrime计算
template <int M, int N>
struct Prime
{
const static bool value = If<!!(M % N), Prime<M, N - 1>, Bool<false> >::Ret::value;
};
//辅助IsPrime计算, 一直算到2终止计算
template <int M>
struct Prime<M, 2>
{
const static bool value = If<!!(M % 2), Bool<true>, Bool<false> >::Ret::value;
};
//判断数N是否为一个素数,如果N是素数则value = true,否则为false
template <int N>
struct IsPrime
{
const static bool value = Prime<N, N-1>::value; //Prime会自动帮我们算出N是否是一个素数
};
//2是素数,但2无法通过Prime来计算判断,所以使用特例化
template <>
struct IsPrime<2>
{
const static bool value = Bool<true>::value;
};
//寻找 <= N 的所有素数,并存放进m_vec中
template <int N>
struct VecPrime
{
VecPrime () { init(m_vec); }
static inline void init(std::vector<int> &vec)
{
VecPrime<N - 1>::init(vec); //在放入N之前先判断 N - 1 是不是素数,等于说是用递归实现了循环
if(IsPrime<N>::value) //如果N是素数,则放入vec中
vec.push_back(N);
}
std::vector<int> m_vec;
};
//数字1比较特殊,特别对待,使用模板特例化
template <>
struct VecPrime<1>
{
static inline void init(std::vector<int> &vec) { }
std::vector<int> m_vec;
};
#define N 250
int main()
{
VecPrime<N> vecPrime; //(不考虑vector容器和数组的细微区别)此处等价于第一种方法中的 int primes[] = { 2, 3, 5, 7, 9, 11, 13 .......... };
for(int i = 0; i < vecPrime.m_vec.size(); ++i)
std::cout << "第" << i + 1 << "个素数是: " << vecPrime.m_vec[i] << std::endl;
return 0;
}
至此,就算是成功使用模板元找到250(包括250)以内的所有素数;
总结:
巧妙的使用模板元,可以将很多计算转移到编译期,从而提升程序的执行速度,但缺点也很明显,需要多码很多代码,而且代码不怎么好阅读,一旦编译报错,很容易让人懵逼,不知道从哪找错
当然,这仅仅是入门,不好做过多的评价;