C++模板的威力
——编译期计算 vs. 运行时计算
C++作为一种语言,更多的时候她被人熟知的是她对面向对象的支持。于是她顶多是C语言的超集,一种扩充而已。而事实上,C++与C是完全不同的语言,这就像C++与java一样。C++作为一种通用的程序设计语言已相当成功的实现了她最初的设计目标:高效率,通用性,可扩展性与灵活性;是一种语言而不是一个系统;她给予程序员尊重而不是束缚等等。其实,C++还支持一种被称为模板元编程的泛型程序设计范型,其核心就是巧妙利用C++的模板机制与面向对象的机制将程序对数据的处理提前到编译期,而不是运行时。这种程序设计方式如同函数式的编程方式。在这种设计范型中没有变量与循环,但其图灵完备所以称其为元编程。
接下来我将举一个简单的例子看看这种程序设计对运行时效率的影响。
有这么一组数列1,1,2,3,5,8,13,21,等等,最前一,二项是1,以后每一项是其前两项的和。现在我们编程计算第N项的数值。程序如下:
//CODE
#include "stdafx.h"
//编译期计算方法
template <int n> //主模板 计算数列第N项
struct f
{
enum {value=f<n-1>::value+f<n-2>::value};
};
template<>
struct f<1> //特化模板 数列第一项是1
{
enum {value=1};
};
template<>
struct f<2> //特化模板 数列第二项是1
{
enum {value=1};
};
//远行时计算方法
long F (long n)
{
if (n==1 || n==2) //数列第一,二项是1直接返回
{
return 1;
}
else //递归
{
return F(n-1)+F(n-2);
}
}
int _tmain(int argc, _TCHAR* argv[])
{
std::cout<<f<45>::value<<std::endl; //计算第45项的数值
std::cout<<F(45)<<std::endl;
return 0;
}
//本程序在Visual C++7.0环境下编译通过,而Visaul C++6.0不行,一些较旧的编译器也不行。
程序分析:
主模板:
template <int n>
struct f
{
enum {value=f<n-1>::value+f<n-2>::value};
};
主模板用于当大于2时的计算。当N大于2时,主模板会一层一层的往下实例化直到某一终止条件。value是一个枚举常量。
特化模板:
template<>
struct f<1> //特化模板 数列第一项是1
{
enum {value=1};
};
template<>
struct f<2> //特化模板 数列第二项是1
{
enum {value=1};
};
特化模板就如同我们循环的终止条件,在模板的递归实例化过程中充当递归终止条件。当主模板的N降为1或2时,模板递归实例化停止,开始计算value的数值。有人会问:为什么当N等于1或2时,编译器会根据特化模板实例化呢?还记得C++对函数的调用规则吗?C++在调用重载函数过程中,编译器会寻求最匹配的一个。其实,在C++中,当编译器发现有众多被选项时,它会选择最符合条件,最匹配的那个。这不是很合理吗?
我想接下来的代码就是你我非常熟悉不过的了,一个递归函数,测试代码。
程序结果:
1134903170
??????
有人会问:问号是什么意思?其实是“不知道”的意思。第一行不到1秒(其实更短)就输出了,而和它一模一样运行时的结果却让我等得花儿都谢了。为什么会这样呢?
编译期计算 vs. 运行时计算
秘密在于?:第一行的结果在程序运行时结果已经早就被算出来了,程序只要简单的输出即可;而它的同胞兄弟还在运行时堆栈里漫游了。模板实例化发生在程序构造阶段——编译期,所以有关的计算都发生在编译期,即编译期计算。而普通的递归函数运行于程序的运行期,其执行要牵涉大量的堆栈操作,有关计算必须在运行时计算。
由以上的直观体验我们可以看到编译期计算对程序运行时的效率提高的影响是巨大的。
——编译期计算 vs. 运行时计算
C++作为一种语言,更多的时候她被人熟知的是她对面向对象的支持。于是她顶多是C语言的超集,一种扩充而已。而事实上,C++与C是完全不同的语言,这就像C++与java一样。C++作为一种通用的程序设计语言已相当成功的实现了她最初的设计目标:高效率,通用性,可扩展性与灵活性;是一种语言而不是一个系统;她给予程序员尊重而不是束缚等等。其实,C++还支持一种被称为模板元编程的泛型程序设计范型,其核心就是巧妙利用C++的模板机制与面向对象的机制将程序对数据的处理提前到编译期,而不是运行时。这种程序设计方式如同函数式的编程方式。在这种设计范型中没有变量与循环,但其图灵完备所以称其为元编程。
接下来我将举一个简单的例子看看这种程序设计对运行时效率的影响。
有这么一组数列1,1,2,3,5,8,13,21,等等,最前一,二项是1,以后每一项是其前两项的和。现在我们编程计算第N项的数值。程序如下:
//CODE
#include "stdafx.h"
//编译期计算方法
template <int n> //主模板 计算数列第N项
struct f
{
enum {value=f<n-1>::value+f<n-2>::value};
};
template<>
struct f<1> //特化模板 数列第一项是1
{
enum {value=1};
};
template<>
struct f<2> //特化模板 数列第二项是1
{
enum {value=1};
};
//远行时计算方法
long F (long n)
{
if (n==1 || n==2) //数列第一,二项是1直接返回
{
return 1;
}
else //递归
{
return F(n-1)+F(n-2);
}
}
int _tmain(int argc, _TCHAR* argv[])
{
std::cout<<f<45>::value<<std::endl; //计算第45项的数值
std::cout<<F(45)<<std::endl;
return 0;
}
//本程序在Visual C++7.0环境下编译通过,而Visaul C++6.0不行,一些较旧的编译器也不行。
程序分析:
主模板:
template <int n>
struct f
{
enum {value=f<n-1>::value+f<n-2>::value};
};
主模板用于当大于2时的计算。当N大于2时,主模板会一层一层的往下实例化直到某一终止条件。value是一个枚举常量。
特化模板:
template<>
struct f<1> //特化模板 数列第一项是1
{
enum {value=1};
};
template<>
struct f<2> //特化模板 数列第二项是1
{
enum {value=1};
};
特化模板就如同我们循环的终止条件,在模板的递归实例化过程中充当递归终止条件。当主模板的N降为1或2时,模板递归实例化停止,开始计算value的数值。有人会问:为什么当N等于1或2时,编译器会根据特化模板实例化呢?还记得C++对函数的调用规则吗?C++在调用重载函数过程中,编译器会寻求最匹配的一个。其实,在C++中,当编译器发现有众多被选项时,它会选择最符合条件,最匹配的那个。这不是很合理吗?
我想接下来的代码就是你我非常熟悉不过的了,一个递归函数,测试代码。
程序结果:
1134903170
??????
有人会问:问号是什么意思?其实是“不知道”的意思。第一行不到1秒(其实更短)就输出了,而和它一模一样运行时的结果却让我等得花儿都谢了。为什么会这样呢?
编译期计算 vs. 运行时计算
秘密在于?:第一行的结果在程序运行时结果已经早就被算出来了,程序只要简单的输出即可;而它的同胞兄弟还在运行时堆栈里漫游了。模板实例化发生在程序构造阶段——编译期,所以有关的计算都发生在编译期,即编译期计算。而普通的递归函数运行于程序的运行期,其执行要牵涉大量的堆栈操作,有关计算必须在运行时计算。
由以上的直观体验我们可以看到编译期计算对程序运行时的效率提高的影响是巨大的。
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1595529