转贴:来自 abp 论坛的精彩文章,对理解 meta-programming 来说是不错的入门材料。

Elminster 2004-06-28 10:51:58
原文比较长,而且代码比较多,这里摘取一部分,原文在:
http://www.allaboutprogram.com/viewtopic.php?t=1992

====================================

I.背景

C++在很多人的心目中,一直是一种OO语言,而事实上,现在对C++的非OO部分的各种使用被逐渐地挖掘出来,其中最大的部分莫过于是template。STL、loki、boost,...,很多先行者为我们提供了方案,有的已经被列入C++标准的一部分。template的一个重要使用方法就是template meta programming,它利用编译器对于template的解释是静态的这一特性,让编译器在编译时做计算,可以有效的提高程序的运行速度。有关于template meta programming的记载,最早见于Erwin Unruh,他在1994年写了一个用template计算质数的程序。我希望通过这篇文章介绍一些TMP的基本技巧和应用,并且最终完成一个质数计算程序。
(阅读本文的过程中,建议你试图编译每一个给出的程序。由于所有的类只需要public成员,所以都用struct声明,但是仍然称之为类。)

II.技术

通常我们编写一个(小)程序,需要的语言支持其实不必很多,只要有顺序、选择和循环三种控制结构理论上就可以写出大多数程序了。我们先用TMP建立一个简单的语言环境。

1.打印

程序有了结果,需要有一个方式反馈给运行者,这里我们利用C++的出错信息,建立一个打印函数。要知道我们希望一切都在编译的时候结束,那么我们就必须让C++编译器在编译信息里面告诉我们,所以我们利用编译器的出错信息。当然这只是一个trick,如果你的TMP只是程序的一部分,你可以使用正常的输入输出。


Code:template<unsigned int value>
struct print
{
static const unsigned int result = (unsigned char*)value;
};


这个类,每当别人引用到它的result的时候,编译器就会打印出错信息,因为一个unsigned int是不能隐式的转成一个unsigned char*的。譬如下面这段程序


Code:template<unsigned int value>
struct print
{
static const unsigned int result = (unsigned char*)value;
};

unsigned int test1 = print<77>::result;
unsigned int test2 = print<123>::result;


在我的Dev C++里,会输出


Code:main.cpp: In instantiation of `print<77>':
main.cpp:7: instantiated from here
main.cpp:4: invalid conversion from `unsigned char*' to `unsigned int'
main.cpp: In instantiation of `print<123>':
main.cpp:8: instantiated from here
main.cpp:4: invalid conversion from `unsigned char*' to `unsigned int'


这个输出虽然不是很好看,但也算是差强人意。

2.选择

Andrei Alexanderescu在他的大作Modern C++ Design里面使用过一个类,可以根据bool的值选择不同的类型。今天我们要写的一个是根据bool的值选择不同的整数。


Code:template<bool condition, unsigned int value1, unsigned int value2>
struct template_if
{
static const unsigned int result = value1;
};

template<unsigned int value1, unsigned int value2>
struct template_if<false, value1, value2>
{
static const unsigned int result = value2;
};


这里用到了模板的特化,如果你对这个不熟悉,那么大致可以这样理解:第一个template_if的定义告诉编译器,“一般的”template_if,会选择第一个值作为结果。第二个template_if告诉编译器,如果第一个参数是false的话,我们就使用第二个值(第三个参数)作为结果。下面这段代码演示了template_if的用法。


Code:template<unsigned int value>
struct print
{
static const unsigned int result = (unsigned char*)value;
};

template<bool condition, unsigned int value1, unsigned int value2>
struct template_if
{
static const unsigned int result = value1;
};

template<unsigned int value1, unsigned int value2>
struct template_if<false, value1, value2>
{
static const unsigned int result = value2;
};

template<unsigned int value>
struct print_if_77
{
static const unsigned int result = template_if<value == 77 , print<value>::result , 0>::result;
};

unsigned int test1 = print_if_77<77>::result;
unsigned int test2 = print_if_77<123>::result;


如果你去编译这段代码的话,你会发觉77和123都被打印出来了,虽然错误信息不一样,但是这不是我们想要的结果。为什么呢?很遗憾,对C++编译器来说,template_if<true, 1, 100>和template<true, 1, 200>是两个不同的类,虽然后一个参数的值我们并不关心,但是编译器必须在template初始化的时候,给出所有的参数,这就导致它会去计算print<value>::result,当然,计算的结果就是报错。也就是说,因为编译器要计算这个值才导致了我们的print不可用,要解决这个问题,有两个方法:或者让编译器不计算这个值,或者让编译器在某些情况下可以计算出正确的值。

方法一可以让编译器不计算这个值,通过修改template_if,我们传入两个不同的类,而不是unsigned int。
首先修改print,加一个新的类dummy_print:


Code:template<unsigned int value>
struct print
{
static const unsigned int result = (unsigned char*)value;
};

template<unsigned int value>
struct dummy_print
{
static const unsigned int result = value;
};


接着,加入一套对类型进行选择的模板:


Code:template<bool condition, typename T1, typename T2>
struct template_if_type
{
static const unsigned int result = T1::result;
};

template<typename T1, typename T2>
struct template_if_type<false, T1, T2>
{
static const unsigned int result = T2::result;
};


这样原先的程序就变成:


Code:template<unsigned int value>
struct print
{
static const unsigned int result = (unsigned char*)value;
};

template<unsigned int value>
struct dummy_print
{
static const unsigned int result = value;
};

template<bool condition, typename T1, typename T2>
struct template_if_type
{
static const unsigned int result = T1::result;
};

template<typename T1, typename T2>
struct template_if_type<false, T1, T2>
{
static const unsigned int result = T2::result;
};

template<unsigned int value>
struct print_if_77
{
static const unsigned int result = template_if_type<value == 77 ,
print<value> , dummy_print<value> >::result;
};

unsigned int test1 = print_if_77<77>::result;
unsigned int test2 = print_if_77<123>::result;


现在的“运行结果”非常正确。

方法二可以让编译器在某些情况下计算出正确的值,我们加一套新的模板:


Code:template<bool condition, unsigned int value>
struct print_if
{
static const unsigned int result = (unsigned char*)value;
};

template<unsigned int value>
struct print_if<false, value>
{
static const unsigned int result = value;
};


原先的程序变为:


Code:template<bool condition, unsigned int value>
struct print_if
{
static const unsigned int result = (unsigned char*)value;
};

template<unsigned int value>
struct print_if<false, value>
{
static const unsigned int result = value;
};

template<unsigned int value>
struct print_if_77
{
static const unsigned int result = print_if<value == 77 , value>::result;
};

unsigned int test1 = print_if_77<77>::result;
unsigned int test2 = print_if_77<123>::result;


输出也是正确的。

这两种方案,我个人倾向于后者,因为其实我们一定是要做一次判断的,并且这次判断一定会添加新的类,那么还是print_if的解决方案比较直观。
...全文
209 7 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
kaphoon 2004-06-29
  • 打赏
  • 举报
回复
什么时候,我有三颗星啊
loveisbug 2004-06-29
  • 打赏
  • 举报
回复
看看原文。
step_by_step 2004-06-29
  • 打赏
  • 举报
回复
昨天刚看,不错
zf0579 2004-06-29
  • 打赏
  • 举报
回复
zan
goodname 2004-06-29
  • 打赏
  • 举报
回复
up2
qwertasdfg123 2004-06-29
  • 打赏
  • 举报
回复
up
zjxiaoyu 2004-06-28
  • 打赏
  • 举报
回复
先mark一下。日后慢慢看

65,186

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧