《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(Metaprogramming Library)进行高阶的模板元编程。
3.1 单位分析
物理计算的首要原则是:数值并非是独立的——大多数物理量都有单位。而我们一不小心就会将单位置之脑后,这是件很危险的事情。随着计算变得越来越复杂,维持物理量的正确单位能够避免诸如“将质量赋给长度”和“将加速度和速度相加”之类不经意间就会犯下的错误。这意味着为数值建立一个类型系统。
手动检查类型是件单调而乏味的工作,并且容易导致错误。当人们感到厌烦时,注意力就会分散,从而容易犯错误。然而,类型检查不正是计算机擅长的工作吗?如果我们能够为物理量和单位构建一个C++型别的framework,那么我们从公式中就可以捕获错误,而不用等到它们在现实世界中导致问题的时候。
阻止单位不同的物理量互操作并不难——我们可以简单地用类来表现单位,并且只允许相同的类(单位)互操作。但是问题远不止这么简单,不同的单位可以通过乘或除结合起来,从而产生一个复杂的新单位,由于可以不断乘除,所以产生的新单位其复杂度几乎是任意的。看来问题变得更有趣了!例如,牛顿定律(它将力,质量,加速度三者联系起来):
F=ma
由于质量和加速度有着不同的单位,所以力的单位必须是两者的结合。事实上,加速度的单位就已经是个“混合物”了——单位时间内速度的改变:
dv/dt
又因为速度即“单位时间内经过的距离”,所以加速度的基本单位是:
(l/t)/t=l/t2
并且,加速度通常以“米每平方秒”来衡量。所以,力的单位为:
ml/t2
也就说,力通常以kg(m/s2)或“千克米每平方秒”来衡量。当我们将质量和加速度相乘时,我们除了将数量相乘之外还必须将单位相乘,这可以帮我们确信结果是有意义的。这种(对单位的)簿记的正式名称为单位分析,而我们的下一个任务就是在C++类型系统中实现它。John Barton和Lee Nackman在它们的著作《Scientific and Engineering C++》中第一次展示了如何实现它。我们将沿袭他们的思路,只不过重新以元编程的方式来实现。
<chsdate isrocdate="False" islunardate="False" day="30" month="12" year="1899" w:st="on"><strong style="mso-bidi-font-weight: normal"><span lang="EN-US" style="FONT-SIZE: 12pt; FONT-FAMILY: "Century Gothic"; mso-bidi-font-family: 'Courier New'">3.1.1</span></strong></chsdate> 单位的表示
国际标准单位制规定了物理量的标准单位为:质量(kg),长度或位置(m),时间(s),电荷(c),温度(oc),密度(kg/m3),角度(o)。为了通用一些,我们的系统必须可以表示七个或七个以上的基本单位,还要能够表示复合单位,比如力(kg(m/s2))的单位这种经过几个基本单位乘除而成的复合单位。
一般来说,一个复合单位可以看成若干基本单位的幂的乘积。如果要表示这些幂次以便在运行期可以操纵它们,我们可以使用一个数组,其七个元素每个对应一个不同的单位,而其值表示对应单位的幂次:
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。然而,如果我们想要将单位融入到类型系统中去,这些数组就无法胜任了:它们的类型全都相同,都是dimension!而我们需要的是自身能够表示数值序列的类型,这样质量和长度的类型就是不同的,而两个质量的类型则是相同的。
幸运的是,MPL提供了一组表示类型序列的设施。例如,我们可以构建一个有符号整型的序列:
#include <boost/mpl/vector.hpp>
typedef boost::mpl::vector<
signed char, short, int, long> signed_types;
那么,我们如何用类型序列来表示单位呢?由于数值型的元函数传递和返回的类型是具有内嵌::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;
<chsdate isrocdate="False" islunardate="False" day="30" month="12" year="1899" w:st="on"><strong style="mso-bidi-font-weight: normal"><span lang="EN-US" style="FONT-SIZE: 12pt; FONT-FAMILY: "Century Gothic"; mso-bidi-font-family: 'Courier New'">3.1.2</span></strong></chsdate> 物理量的表示
上面所列的类型仍然是纯粹的元数据。要想对真实的计算进行类型检查,我们还需要以某种方式将它们(元数据)绑定到运行时数据。一个简单的数值外覆类——模板参数为数据类型T和T的单位——刚好合适:
template <class T, class Dimensions>
struct quantity
{
explicit quantity(T x)
: m_value(x)
{}
T value() const { return m_value; }
private:
T m_value;
};
现在,我们有了将数值和单位联系到一起的办法。例如,我们可以说:
quantity<float,length> l(<chmetcnv w:st="on" unitname="F" sourcevalue="1" hasspace="False" negative="False" numbertype="1" tcsc="0">1.0f</chmetcnv>);
quantity<float,mass> m(<chmetcnv w:st="on" unitname="F" sourcevalue="2" hasspace="False" negative="False" numbertype="1" tcsc="0">2.0f</chmetcnv>);
注意到在quantity的类定义体中并没有出现Dimensions模板参数的任何身影,它只在模板参数列表中出现过,其唯一作用是确保l和m具有不同的类型。这样,我们就不可能错误地将长度赋给质量:
<chsdate isrocdate="False" islunardate="False" day="30" month="12" year="1899" w:st="on"><strong style="mso-bidi-font-weight: normal"><span lang="EN" style="FONT-SIZE: 12pt; FONT-FAMILY: "Century Gothic"; mso-ansi-language: EN; mso-bidi-font-family: 'Courier New'">3.1.3</span></strong></chsdate> 实现加法和减法
因为参数的类型(单位)必须总是匹配,所以我们现在可以轻易的写出加法和减法的规则:
template <class T, class D>
quantity<T,D>
operator+(quantity<T,D> x, quantity<T,D> y)
{
return quantity<T,D>(x.value() + y.value());
}
template <class T, class D>
quantity<T,D>
operator-(quantity<T,D> x, quantity<T,D> y)
{
return quantity<T,D>(x.value() - y.value());
}
这样,我们就可以写出类似下面的代码:
quantity<float,length> len1(<chmetcnv w:st="on" unitname="F" sourcevalue="1" hasspace="False" negative="False" numbertype="1" tcsc="0">1.0f</chmetcnv>);
quantity<float,length> len2(<chmetcnv w:st="on" unitname="F" sourcevalue="2" hasspace="False" negative="False" numbertype="1" tcsc="0">2.0f</chmetcnv>);
len1 = len1 + len2; //ok
并且,我们不能将不同单位的量相加:
len1 = len1 = quantity<float, mass>(<chmetcnv w:st="on" unitname="F" sourcevalue="3.7" hasspace="False" negative="False" numbertype="1" tcsc="0">3.7f</chmetcnv>); // error
<chsdate isrocdate="False" islunardate="False" day="30" month="12" year="1899" w:st="on"><strong style="mso-bidi-font-weight: normal"><span lang="EN-US" style="FONT-SIZE: 12pt; FONT-FAMILY: "Century Gothic"; mso-bidi-font-family: 'Courier New'">3.1.4</span></strong></chsdate> 实现乘法
乘法比加减法复杂一些。到目前为止,运算的参数和结果的单位都是一样的,但是做乘法时,结果的单位往往和两个参数的单位都不相同。对于乘法,下面的式子:
意味着结果的单位的指数为相应参数的单位的指数和。商与此类似,为指数差。
为此,我们使用MPL的transform算法来将两个序列中的对应元素相加。transform是个元函数,它遍历两个并行的输入序列,对于每个位置将两个序列中的对应元素传给一个任意的(用户提供的)二元元函数,并且将结果存入一个输出序列。
template <
class Sequence1,
class Sequence2,
class BinaryOperation
border-right: medium none; padding-right: 0cm; border-top: medium none; padding-left: 0cm; padding-bottom: 0cm; margin: 0cm 0cm 0pt; border-left: medium none; padding-top: 0
分享到:
相关推荐
《C++ Template Metaprogramming Concepts, Tools, and Techniques from Boost and Beyond》是关于C++元编程的一本经典教材,适合那些对软件构造有深入兴趣的读者。元编程是C++语言的一个强大特性,它允许程序员在...
本资源——"C++ Template Metaprogramming"——提供了一个深入探讨这一领域的英文版指南。 模板是C++语言中的一个关键特性,它允许我们定义函数和类的通用模板,然后在不同类型的参数上实例化它们。模板元编程则是...
本书主要介绍Traits和类型操纵、深入探索元函数、整型外覆器和操作、序列与迭代器、算法、视图与迭代器适配器、诊断、跨越编译期和运行期边界、领域特定的嵌入式语言、DSEL设计演练,另外附录部分还介绍了预处理元...
《Addison.Wesley.C++.Template.Metaprogramming.Concepts.Tools.and.Techniques》这本书深入探讨了C++模板元编程这一高级编程技术。模板元编程是C++语言中的一个独特领域,它允许程序员在编译时执行计算,极大地...
自从C++引入了泛型编程以来,程序员们就开始探索各种“模板技巧”,这些技巧使得在编译过程中可以有效地消除程序与元程序之间的界限。虽然这些能力在C++专家中引起了广泛关注,并逐渐传播到了更广泛的社区中,但对于...
模板元编程(Template Metaprogramming)是利用模板在编译期间进行计算的一种技术。它使得程序员可以在编译时生成代码,提供了一种静态多态的手段。 6. **模板的隐式类型转换和SFINAE原则** 在模板实例化过程中,...
本文将深入探讨C++模板元编程(Template Metaprogramming, TMP)与函数式编程(Functional Programming, FP)之间的联系。通过分析博士论文《C++模板元编程与函数式编程的联系》中的内容,我们可以更清晰地理解这两...
此外,模板元编程(Template Metaprogramming)也可能被提及,这是一种利用模板在编译时执行计算的技术,可以用于实现强大的类型安全工具和库,如Boost库。 在深入学习模板之前,你需要理解C++的基础知识,包括数据...
在C++领域,模板元编程(Template Metaprogramming, TMP)作为一种强大的编程技术已经得到了广泛的应用。它允许程序员在编译时进行代码生成和操作,从而实现高效的程序设计。本书《C++ Template Metaprogramming: ...
模板元编程(Template Metaprogramming,TMP)由Erwin Unruh在1994年的C++标准委员会会议上提出,通过利用模板的可递归性和特化功能,可以在编译阶段执行复杂的逻辑。这一概念随后被Todd Veldhuizen正式命名为“模板...
### Boost C++ Metaprogramming Library #### 一、引言 本文介绍的是Boost C++模板元编程库(MPL),这是一个可扩展的编译时框架,由算法、序列和元函数类组成。该库结合了泛型编程和函数式编程世界中的重要抽象,...
模板元编程(Template Metaprogramming,TMP)是C++模板的高级用法。它利用编译期计算,允许在编译阶段执行类似运行时的逻辑。TMP可以创建复杂的类型系统,如类型检查、类型转换和运行时无法实现的优化。然而,由于...
6. **模板元编程(Template Metaprogramming)**: C++ 的模板机制也可以用于在编译时执行计算,这就是所谓的模板元编程。它允许程序员编写在编译期间运行的代码,从而生成高效且无运行时开销的程序。 7. **SFINAE...
历史部分提到了模板元编程的起源,1994年Erwin Unruh通过一段展示在编译期计算质数的代码引起了关注,随后Todd Veldhuizen进一步发展了这一概念,将其命名为C++模板元编程(Template Metaprogramming,TMP)。...
13. **模板元编程(Template Metaprogramming)**:在编译时进行计算和编程,可以创建高度定制的类型系统。 14. **协程(Coroutines)**:C++20引入,提供了一种轻量级的并发机制,允许函数在执行过程中暂停并恢复。 ...
七、模板元编程(Template Metaprogramming) 模板元编程是在编译期间进行计算的技术,可以生成复杂的数据结构和算法。Boost库中的 MPL(Metaprogamming Library)是模板元编程的一个典型应用。 八、C++11及其后续...
1. **STL(Standard Template Library)标准模板库**:STL是C++的核心组成部分,包括容器(如vector、list、set等)、迭代器、算法和函数对象。理解如何有效地使用STL可以极大地提高代码的可读性和性能。 2. **模板...