刘未鹏|C++的罗浮宫

Knowledge sharing is the best reuse

刘未鹏ID:pongba
883562次访问,排名36好友14人,关注者152
pongba的文章
原创 101 篇
翻译 8 篇
转载 0 篇
评论 1730 篇
刘未鹏的公告
除非特别声明,本站采用Creative Commons License许可。转载请保留作者、出处。非商业。

FeedSkyFeedBurner
或者用 鲜果 GR 抓虾 订阅。

CSDN Blog暂时不支持RSS全文输出,对此感到不便的朋友可以使用强大的greasemonkey脚本:GReader Preview Enhanced(链接),该脚本支持在GReader里面直接打开全文页面。

P.S. 我经常出没于TopLanguage讨论组

《C++的罗浮宫》5年选集(下载)

——知识分享是最大的复用


讨论问题请到TopLanguage小组

TopLanguage


gtalk/msn(邮件请发送到gmail邮箱)

pongba@gmail.com
pp_liu@msn.com

豆瓣 饭否 美味书签

搜索C++的罗浮宫上的内容(不要回车,点击Go)

twitters

books I've translated




这个Blog上都写了哪些东东

最近评论
fallening:OOps, the discussions seems much more inviting than the article.
mutemob:真是无语,什么东西非得往大了去扯,足见夸夸其谈是在校学生的通病,希望上面几位研究哲学的人,能真的老老实实做学问做下去,不要一毕业就直接去考公务员,每天想关如何运用哲学的思想如何提干往上爬,那当真就是要笑翻一片人了,像我这小程序员,学学算法,学学数学,学学思维方式,足已!
roger_77:学习了。多线程时代的共享数据读写
stonewang:很精彩,谢谢分享
rentaocc:深有感触,其实父母的赞扬对培养孩子的自信很重要
文章分类
收藏
相册
其它图片
文章中的图片
我的大头贴
C++
Andrei Alexandrescu
Andrew Lumsdaine
Bjarne Stroustrup
boost
C++ Standard Commitee
Doug Gregor
Hans J. Boehm
Jaakko Jarvi
Jeremy G. Siek
Matthew Wilson
newsgroups
boost.Developer
boost.User
comp.lang.c++.moderated
comp.std.c++
TopLanguage
Open Source
codeplex
Google AJAX Search API
Google Code Prettify
Google Web Toolkit
MS shared source initiative
notepad++
STLSoft
不认识的朋友们
fatalerror99
Glacier
realazy
SpiritEpic
TK
Yelz
YongSun
余晟|乱象&乱想
刘慈欣
吴欣安(atppp)
姬十三
张志强|阅微堂
许式伟
阮一峰
高远
鲍志云
其它
科学松鼠会
科学美国人
科幻世界
认识的朋友们
alai
chenyufei
dd
duguguiyu|Venus神庙
Googol
Joyfire
littlestone
lxwde
Matrix67
realfun
soloist
Tinyfool
windstorm
云风
冯大辉(Fenng)
刘新宇
刘江@图灵
史晓明
周星星
周筠@博文视点
孟岩
张振
徐宥|4G Spaces&Web 2.3
方舟@博文视点
李笑来|Pure Pleasure
杨文博
王信文|地球没有好朋友
荣耀
莫华枫
袁泳(g9)|负暄琐话
谢东升
陈冀康@华章
存档
订阅我的博客
XML聚合  FeedSky
订阅到鲜果
订阅到Google
订阅到抓虾
订阅到BlogLines
订阅到Yahoo
订阅到GouGou
订阅到飞鸽
订阅到Rojo
订阅到newsgator
订阅到netvibes

翻译 《C++ Template Metaprogramming》第三章——深度探索元函数收藏

新一篇: 《C++ Template Metaprogramming》译序——经典之后的经典 | 旧一篇: C++标准库的一个有趣的小bug

C++ Template Metaprogramming

第三章:深度探索元函数

 

By David Abraham

(http://www.boost.org/people/dave_abrahams.htm)

 

By 刘未鹏(pongba)

C++的罗浮宫(http://blog.csdn.net/pongba)

 

原文链接(http://www.boost-consulting.com/mplbook)

 

C++模板元编程是C++最高阶最抽象也是最强大的能力,也是最前卫的技术,其带来的复用性以及代码强大的表达能力令人瞠目结舌,本章深入讨论C++元编程的机理以及应用,展现C++中最高阶的语义...

 

有了前面的基础知识作铺垫,我们来考察模板元编程技术的一个最基本的应用——为传统的不进行类型检查的操作添加静态类型检查。为此,我们将考察一个有关工程科学的实例——几乎在所有涉及科学计算的代码中都可以找到它的应用。在考察该例子的过程中,你将会学到一些重要的新的concepts,并且尝试使用MPL[1]Metaprogramming Library)进行高阶的模板元编程。

 

3.1 单位[2]分析

物理计算的首要原则是:数值并非是独立的——大多数物理量都有单位。而我们一不小心就会将单位置之脑后,这是件很危险的事情。随着计算变得越来越复杂,维持物理量的正确单位能够避免诸如将质量赋给长度将加速度和速度相加之类不经意间就会犯下的错误。这意味着为数值建立一个类型系统。

 

手动检查类型是件单调而乏味的工作,并且容易导致错误。当人们感到厌烦时,注意力就会分散,从而容易犯错误。然而,类型检查不正是计算机擅长的工作吗?如果我们能够为物理量和单位构建一个C++型别的framework,那么我们从公式中就可以捕获错误,而不用等到它们在现实世界中导致问题的时候。

 

阻止单位不同的物理量互操作并不难——我们可以简单地用类来表现单位,并且只允许相同的类(单位)互操作。但是问题远不止这么简单,不同的单位可以通过乘或除结合起来,从而产生一个复杂的新单位,由于可以不断乘除,所以产生的新单位其复杂度几乎是任意的。看来问题变得更有趣了!例如,牛顿定律(它将力,质量,加速度三者联系起来):

 

F=ma

 

由于质量和加速度有着不同的单位,所以力的单位必须是两者的结合。事实上,加速度的单位就已经是个混合物——单位时间内速度的改变:

 

dv/dt

 

又因为速度即单位时间内经过的距离,所以加速度的基本单位是:

 

(l/t)/t=l/t2

 

并且,加速度通常以米每平方秒来衡量。所以,力的单位为:

 

ml/t2

 

也就说,力通常以kg(m/s2)千克米每平方秒来衡量。当我们将质量和加速度相乘时,我们除了将数量相乘之外还必须将单位相乘,这可以帮我们确信结果是有意义的。这种(对单位的)簿记的正式名称为单位分析,而我们的下一个任务就是在C++类型系统中实现它。John BartonLee Nackman在它们的著作《Scientific and Engineering C++》中第一次展示了如何实现它。我们将沿袭他们的思路,只不过重新以元编程的方式来实现。

 

3.1.1 单位的表示

国际标准单位制规定了物理量的标准单位为:质量(kg),长度或位置(m),时间(s),电荷(c),温度(oc),密度(kg/m3),角度(o)。为了通用一些,我们的系统必须可以表示七个或七个以上的基本单位,还要能够表示复合单位,比如kg(m/s2))的单位这种经过几个基本单位乘除而成的复合单位。

 

一般来说,一个复合单位可以看成若干基本单位的幂的乘积[3]。如果要表示这些幂次以便在运行期可以操纵它们,我们可以使用一个数组,其七个元素每个对应一个不同的单位,而其值表示对应单位的幂次:

 

typedef int dimension[7]; //m l t ...

dimension const mass    ={1,0,0,0,0,0,0};

dimension const length  ={0,1,0,0,0,0,0};

dimension const time    ={0,0,1,0,0,0,0};

...

 

根据这种表示法,力的表示如下:

 

dimension const force = {1,1,-2,0,0,0};

 

也就是说,mlt-2。然而,如果我们想要将单位融入到类型系统[4]中去,这些数组就无法胜任了:它们的类型全都相同,都是dimension!而我们需要的是自身能够表示数值序列的类型,这样质量和长度的类型就是不同的,而两个质量的类型则是相同的。

 

幸运的是,MPL提供了一组表示类型序列的设施。例如,我们可以构建一个有符号整型的序列:

 

#include <boost/mpl/vector.hpp>

 

typedef boost::mpl::vector<

signed char, short, int, long> signed_types;

 

那么,我们如何用类型序列来表示单位[5]呢?由于数值型的元函数传递和返回的类型是具有内嵌::value的外覆类,所以数值序列其实是外覆类型的序列(另一个多态的例子)。为了使事情变得更为简单,MPL提供了int_<N>类模板,它以一个内嵌的::value来表现它的整型参数N

 

#include <boost/mpl/int.hpp>

 

namespace mpl = boost::mpl;[6] // namespace alias

static int const five = mpl::int_<5>::value;

 

事实上,MPL库包含了一整套整型常量的外覆类,如long_bool_等,每个外覆类对应一个不同类型的整型常量。

 

现在,我们可以将基本单位构建如下:

 

typedef mpl::vector<

mpl::int_<1>, mpl::int_<0>, mpl::int_<0>, mpl::int_<0>

 , mpl::int_<0>, mpl::int_<0>, mpl::int_<0>

> mass;

 

typedef mpl::vector<

mpl::int_<0>, mpl::int_<1>, mpl::int_<0>, mpl::int_<0>

 , mpl::int_<0>, mpl::int_<0>, mpl::int_<0>

> length;

...

 

...你很快就会觉得这写起来实在太累人。更糟糕的是,这样的代码难于阅读和验证。代码的本质信息,也就是每个基本单位的幂次,被埋在重复的语法噪音中。因此,MPL相应还提供了整型序列外覆类,它允许我们写出类似下面的代码:

 

#include <boost/mpl/vector_c.hpp>

 

typedef mpl::vector_c<int,1,0,0,0,0,0,0> mass;

typedef mpl::vector_c<int,0,1,0,0,0,0,0> length; // or position

typedef mpl::vector_c<int,0,0,1,0,0,0,0> time;

typedef mpl::vector_c<int,0,0,0,1,0,0,0> charge;

typedef mpl::vector_c<int,0,0,0,0,1,0,0> temperature;

typedef mpl::vector_c<int,0,0,0,0,0,1,0> intensity;

typedef mpl::vector_c<int,0,0,0,0,0,0,1> angle;

 

你可以将这个特殊的mpl::vector_c看作与前面那个冗长的mpl::vector一样,尽管它们的类型并不相同。

 

如果我们愿意,我们还可以定义一些复合单位:

 

// 基本单位:m l t ...

typedef mpl::vector_c<int,0,1,-1,0,0,0,0> velocity; // l/t

typedef mpl::vector_c<int,0,1,-2,0,0,0,0> acceleration;

// l/(t2)

typedef mpl::vector_c<int,1,1,-1,0,0,0,0> momentum; // ml/t

typedef mpl::vector_c<int,1,1,-2,0,0,0,0> force; // ml/(t2)

 

并且,有时候,标量的单位(如pi,标量的单位即没有单位——译注)也可以这样来描述:

 

typedef mpl::vector_c<int,0,0,0,0,0,0,0> scalar;

 

3.1.2 物理量的表示

上面所列的类型仍然是纯粹的元数据。要想对真实的计算进行类型检查,我们还需要以某种方式将它们(元数据)绑定到运行时数据。一个简单的数值外覆类——模板参数为数据类型TT的单位——刚好合适:

 

template <class T, class Dimensions>

struct quantity

{

explicit quantity(T x)

: m_value(x)