分享
 
 
 

C++ 的MetaProgramming 入门篇(2)

王朝c/c++·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

上回说到一个fac的版本, 希望在负数的情况下返回-1, 而不是无限递归下去.

还是按照我们的思维, 先写个对应"运行时世界"的版本.

int safe_fac(int n)

{

if( n < 1)

return -1;

return fac(n);

}

这个if逻辑很简单, 如果模板参数<1, 那么直接返回 -1, 否则 还是使用前面的fac那个版本.

好, 转换成我们的meta 版本.

你想,用个 ?: 运算符不就解决了吗?

template<int n>

struct safe_fac

{

enum { value = (n < 1 ? -1 : fac<n>::value ) };

};

可惜不对, ?= 只有在"运行时世界"才能使用. 那么 value 后面的???写什么好呢?

先轻松轻松, 写一个if的meta 版本, 我敢保证你能看得懂.

template< bool b , class T, class U>

struct if_

{

typedef T type;

};

注意了, 如果以前我们提到的例如sum_, fac等meta functions(其实就是c++中的模板类, 称之为meta function是因为它们就像是function)

是通过一个 在enum中的value 返回整形的话, 上面刚刚的if_这个例子就展示了 meta中的另外一个武器, 通过typedef 一个type 返回一个类型.

如果我们这样调用

if_<true, int, double>::type 的结果就是 int 类型, 注意是"类型", 不是对象.

我们想在b为false的时候返回第二个类型U, 即:

if_<false, int, double>::type 的返回结果是double

那么还是很简单, 部分特化 b 参数就可以了.即:

template<class T, class U>

struct if_<false, T, U>

{

typedef U type;

};

我最前面说了, VC6不支持部分特化, 但是别忘了计算机时间的一条公理:

任何计算机问题都可以通过增加一层来解决. 大部分VC6中的模板的问题还是可以解决的. 如果你不使用VC6, 这部分就不用看了.

VC6是支持全部特化的, 因此我们可以将true, false特化出来

template<bool>

struct if_help

{

...

};

template<>

struct if_help<false>

{

...

};

这个在vc6中是支持的. 然后我们还需要两个额外的类型参数T,U, 这可以通过嵌套类来实现. 即

template<bool>

struct if_help

{

template<class T, class U>

struct In

{

typedef T type;

};

};

template<>

struct if_help<false>

{

template<class T, class U>

struct In

{

typedef U type;

};

};

然后我们真正的if_ "meta 函数"如下定义:

template<bool b, class T, class U>

struct if_

{

typedef if_help<b>::In<T, U>::type type;

};

先根据b的内容实例化一个对应的if_help, : if_help<b>

然后给其中的In模板投递T,U参数, ::In<T, U>

然后通过type获得其中的返回类型 ::type

最后typedef type一下作为自己的返回类型, 这样外部就可以通过

if_<true, int, double>::type 获得返回的类型了.

上面if_ 的实现实际上要用几个C++关键字修饰一下:

typedef if_help<b>::In<T, U>::type type;

===>

typedef typename if_help<b>::template In<T, U>::type type;

为什么要加上typename 和 template, 这个解释起来到是很费劲. 有空再说.

好了, 从模板的语法世界中清醒过来, 现在你知道的是, 我们有了一个if_ 的meta函数, 接受3个参数bool b, class T, class U,

如果b为true, 那么它的返回值 (通过 if_::type 返回) 就是T,

如果b为false, 那么它的返回值 (通过 if_::type 返回) 就是U.

前面我提过了, 参数是通过<>来传递的, 因此一个例子就是

if_ //函数名

if_<true, //第一个参数, bool 型

if_<true, int //第二个参数, 类型

if_<true, int, double //第3个参数, 类型

if_<true, int, doubble> //右括号表示参数结束

if_<true, int, double>::type //通过::type获得返回结果, 不是value了, 当然这仅仅是一个命名惯例.

因此上面的那个 if_<true, int, double>::type 返回的就是 int,

在"运行时世界", 你可以如下使用:

for( if_<true, int, double>::type i = 0; i < 10; i++) {

cout << "line " << i << "\n";

}

等同于

for( int i = 0; i < 10; i++) {

cout << "line " << i << "\n";

}

当然对于这个例子这样使用是"有病". 我们等会会用到if_来实现前面的 safe_fac 实现了.

注意我说的返回值并不是前面sum_例子中的整形了, 这个时候返回一个类型. 类型不是实例对象, 这点我想你应该清楚.

编译时不可能返回对象, 因为要调用构造函数, 要确定对象地址, 我们还没有进入到" 运行时世界" , 对吧?

实际上meta programming 最重要的使用并不是前面我们提到过的sum_, fac这些, 因为毕竟拿个计算器算一下也花不了几个时间.

但是返回type就不同了.

那么type可以是什么呢? 可以是int, double这样的基本类型, 也可以我们前面的 sum_ 模板类等等.

然后再看safe_fac的实现:

先不考虑 <1 的情况, 那么value就应该是直接调用以前的 fac 函数

template<int n>

struct safe_fac

{

enum { value = fac<n>::value };

};

然后再使得 >= 1时才使用fac函数, 那么利用我们前面的if_, 先忽略语法错误, 那么可以如下

enum

{

value = if_< n < 1,???, fac<n> >::type::value

};

首先, n < 1 为真时返回一个类型???, 暂时我们还没有实现, 为false时返回

fac<n>类型, 然后通过::type获得返回的类型,或者为???, 或者为fac<n>,

然后通过::value得到这个类型的整数结果.

那么 ??? 应该是什么呢? 当然不能直接是-1, 否则 -1::value 就是语法错误.

因此我们可以定义一个简单的"函数", 返回-1:

struct return_neg1

{

enum { value = -1 };

};

如果我们需要返回-2怎么办? 又定义一个return_neg2 "函数"? 干脆我们一劳永逸, 定义如下:

template<int n>

struct int_

{

enum { value = n };

};

这样int_ 这个"函数"就是你给我什么, 我就返回什么. 不过int_是一个类型, 例如:

通过如下调用 int_<3>::value 返回它的结果3.

有了这个, 我们的代码就如下:

value = if_< n < 1, int_<-1>, fac<n> >::type::value

原理清楚了, 最终的版本就是:

template<int n>

struct safe_fac

{

enum { value = if_< n < 1, int_<-1>, fac<n> >::type::value };

};

试试:

cout << safe_fac<-1>::value 输出 -1.

循环(递归表示), 条件判断, 顺序执行都有了, 剩下的就看你自己了.

--------------------------------------------------------------------------------

Boost中的mpl (meta programming library) 提供了一个专门用于metaprogramming的library, 同时前面提到的if_, int_等等就

是从mpl中拷贝来的, 当然我简化了很多.

Modern C++ Design 其中的Typelist将meta progamming的循环(就是递归)发挥得淋漓尽致, 在侯捷的网站上www.jjhou.com有免费的前4章可读. Typelist在第三章.

书中序言部分, Effective C++的作者Meyers说到, 如果第3章的typelists没有让你感到振奋, 那你一定是很沉闷.

就我亲身体验, 我觉得Meyers可能是说到委婉了些:

如果第3章的typelists没有让你感到振奋, 那1%的可能是你是很沉闷, 99%的可能是你没有看懂. I did.

GoF之一的 John Vlissides同样提到了typelists这一章就值得本书的价格. I believe.

另外, 我发现即使在这本让人抓狂的天才之作中, 作者仍然使用了n个TYPELIST_n 这样的预处理宏来处理多个type的情况, 但是在sourceforge

下的Loki库中我发现已经有了一个MakeTypelist"函数", 看来 meta programming 确实是, 啊...

是不是作者当时都没有预见到还能够以C++编译器内建的模板能力, 而不是依赖预处理宏来处理typelist.?

又, mpl中有专门装type的容器....

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有