# 3章

4 篇文章 0 订阅
david abraham&Aleksey Gurtovoy著

刘未鹏(pp_liu@msn.com)

3.1 单位[2]分析

F=ma

dv/dt

(l/t)/t=l/t2

ml/t2

3.1.1 表示单位

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};

#include <boost/mpl/vector.hpp>

typedef boost::mpl::vector<

signed char, short, int, long> signed_types;

#include <boost/mpl/int.hpp>

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

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

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;

//基本单位: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)

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

3.1.2 物理量的表示

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( 1.0f );

quantity<float,mass>    m( 2.0f );

m = l; //编译期错误

3.1.3 实现加法和减法

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( 1.0f );

quantity<float,length> len2( 2.0f );

len1 = len1 + len2; //ok

len1 = len1 = quantity<float,mass>( 3.7f ); //error

3.1.4 实现乘法

(Xa)(Xb) = X(a+b)

template <class Sequence1,

class Sequence2,

class BinaryOperation

>

struct transform; //return a sequence

template <

class InputIterator1, class InputIterator2

, class OutputIterator, class BinaryOperation

>

void transform(

InputIterator1 start1, InputIterator2 finish1

, InputIterator2 start2

, OutputIterator result, BinaryOperation func);

#include <boost/static_assert.hpp>

#include <boost/mpl/plus.hpp>

#include <boost/mpl/int.hpp>

namespace mpl = boost::mpl;

BOOST_STATIC_ASSERT((

mpl::plus<

mpl::int_<2>

, mpl::int_<3>

>::type::value == 5

));

BOOST_STATIC_ASSERT是一个宏，如果其参数为false，则会导致一个编译期错误。双括号是必要的，因为C++预处理器不能解析模板：如果不多加一对括号，那么它会将隔开模板参数的逗号当成隔开宏参数的逗号，从而将条件表达式错误地解析为若干宏参数。这和运行期的assert(...)不一样（后者是由C++编译期解析的，可以识别一切表达式——译注），BOOST_STATIC_ASSERT也可以用于类的定义域中，从而允许我们将其置于元函数中。第8章对此有更深入的讨论。

#include <boost/mpl/transform.hpp>

template <class T, class D1, class D2>

quantity<

T

, typename mpl::transform<D1,D2,mpl::plus>::type

>

operator*(quantity<T,D1> x, quantity<T,D2> y) { ... }

struct plus_f

{

template <class T1, class T2>

struct apply

{

typedef typename mpl::plus<T1,T2>::type type;

};

};

定义：元函数类是指内嵌有名为applypublic 元函数的类。

template <class T, class D1, class D2>

quantity<

T

, typename mpl::transform<D1,D2,plus_f>::type //new dimensions

>

operator*(quantity<T,D1> x, quantity<T,D2> y)

{

typedef typename mpl::transform<D1,D2,plus_f>::type dim;

return quantity<T,dim>( x.value() * y.value() );

}

quantity<float,mass> m( 5.0f );

quantity<float,acceleration> a( 9.8f );

std::cout << "force = " << (m * a).value();

mpl::vector_c<int,1,1,-2,0,0,0,0> //kgms-2

quantity<float,force> f = m*a;

template <class T, class Dimensions>

struct quantity

{

// converting constructor

template <class OtherDimensions>

quantity(quantity<T,OtherDimensions> const& rhs)

: m_value(rhs.value())

{

}

...

//m*a的结果应该是力(force)，而非质量(mass)

quantity<float,mass> bogus = m * a;

template <class OtherDimensions>

quantity(quantity<T,OtherDimensions> const& rhs)

: m_value(rhs.value())

{

BOOST_STATIC_ASSERT((

mpl::equal<Dimensions,OtherDimensions>::type::value

));

}

3.1.5 实现除法

struct minus_f

{

template <class T1, class T2>

struct apply

: mpl::minus<T1,T2> {};

};

typedef typename ...::type type

typename mpl::transform<D1,D2,mpl::minus<_1,_2> >::type

#include<boost/mpl/placeholder.hpp>

using namespace mpl::placeholders;

template <class T, class D1, class D2>

quantity<

T

, typename mpl::transform<D1,D2,mpl::minus<_1,_2> >::type

>

operator/(quantity<T,D1> x, quantity<T,D2> y)

{

typedef typename

mpl::transform<D1,D2,mpl::minus<_1,_2> >::type dim;

return quantity<T,dim>( x.value() / y.value() );

}

template <class D1, class D2>

struct divide_dimensions

: mpl::transform<D1,D2,mpl::minus<_1,_2> > //再次转发

{};

template <class T, class D1, class D2>

quantity<T, typename divide_dimensions<D1,D2>::type>

operator/(quantity<T,D1> x, quantity<T,D2> y)

{

return quantity<T, typename divide_dimensions<D1,D2>::type>(

x.value() / y.value());

}

quantity<float,mass> m2 = f/a;

float rounding_error = std::abs((m2-m).value());

3.2 高阶元函数（Higher-Order Metafunctions

twice(f,x) := f(f(x))

template <class F, class X>

struct twice

{

typedef typename F::template apply<X>::type once; //f(x)

typedef typename F::template apply<once>::type type;//f(f(x))

};

template <class F, class X>

struct twice

: F::template apply<

typename F::template apply<X>::type

>

{};

C++语言.附注

C++标准要求：当我们使用依赖名字（dependent name并且该名字指的是一个成员模板时，我们必须使用template关键字。F::apply不一定指的是个模板名字，其含义依赖F。而F::template apply则确切的告诉编译器apply（应该）是个成员模板。关于template，附录B有更多信息。

template <class UnaryMetaFunctionClass, class Arg>

struct apply1

: UnaryMetaFunctionClass::template apply<Arg>

{};

template <class F, class X>

struct twice

: apply1<F, typename apply1<F,X>::type>

{};

{

template <class T>

};

BOOST_STATIC_ASSERT((

boost::is_same<

, int**

>::value

));

3.3 处理占位符

template <class X>

struct two_pointers

{};

template<class T>

{

typedef T* type;

}

3.3.1 lambda元函数

template <class X>

struct two_pointers

{};

BOOST_STATIC_ASSERT((

boost::is_same<

typename two_pointers<int>::type

, int**

>::value

));

template <class F, class X>

struct twice

: apply1<

typename mpl::lambda<F>::type

, typename apply1<

typename mpl::lambda<F>::type

, X

>::type

>

{};

int* x;

3.3.2 a pply元函数

#include <boost/mpl/apply.hpp>

template <class F, class X>

struct twice

: mpl::apply<F, typename mpl::apply<F,X>::type>

{};

1apply1只能操作元函数类，而mpl::apply的第一个参数可以是任意的lambda表达式（包括占位符表达式）[9]

2apply1只能接受除元函数类之外的1个额外参数，并将这个参数传给元函数类。而mpl::apply可以接受15个额外的参数[10]，并用它们来调用元函数类。例如：

//将二元的lambda表达式应用到另外两个参数上

mpl::apply<

mpl::plus<_1,_2>

,mpl::int_<6>

,mpl::int_<7>

>::type::value // == 13

3.4 lambda的其它能力

lambda表达式的能力并不止于使元函数成为可传递的参数。下面介绍的另外两种能力使lambda表达式成为几乎每个元编程任务中不可或缺的部分。

3.4.1 部分函数应用（Partial Metafunction Application

mpl::plus<_1,mpl::int_<42> >

3.4.2 复合元函数[12]Metafunction Composition

lambda表达式也可以被用于组合简单的元函数以产生更为有趣的运算。例如，下面的表达式将两个数的和与差相乘（即(a+b)*(a-b)——译注）：

mpl::multiplies<mpl::plus<_1,_2>, mpl::minus<_1,_2> >

3.5 Lambda的细节

3.5.1 占位符

“占位符”的定义可能会吓你一跳：

3.5.1 .1 实现（Implementation

_1_2_3这些名字只不过是为了方便起见，其实它们是mpl::arg的特化版本的typedefsmpl::arg<N>作为元函数的作用是选出（并返回）它的第N个参数[15]。占位符的实现像这样：

namespace boost {

namespace mpl {

namespace placeholders {

template <int N> struct arg; // 前导声明

struct void_;

template <>

struct arg<1>

{

template <

class A1, class A2 = void_, ... class Am = void_>

struct apply

{

typedef A1 type; // 返回其第一个参数

};

};

typedef arg<1> _1;

template <>

struct arg<2>

{

template <

class A1, class A2, class A3 = void_, ...class Am = void_

>

struct apply

{

typedef A2 type; //返回其第二个参数

};

};

typedef arg<2> _2;

//其它特化版本和typedefs...

}}}

3.5.1 .2 匿名（Unnamed）占位符

namespace boost { namespace mpl { namespace placeholders {

typedef arg<-1> _; //匿名占位符

}}}

3.1

 mpl::plus<_,_> mpl::plus<_1,_2> boost::is_same<           _          ,boost::add_pointer<_>      > boost::is_same<           _1          ,boost::add_pointer<_1>      > mpl::multiplies<          mpl::plus<_,_>         ,mpl::minus<_,_>      > mpl::multiplies<          mpl::plus<_1,_2>         ,mpl::minus<_1,_2>      >

3.5.2 占位符表达式的定义

一个占位符

一个其参数至少有一个为占位符表达式的模板特化体。

3.5.3 lambda和非元函数（Non-Metafunction）模板

// trivial std::vector generator

template<class U>

struct make_vector { typedef std::vector<U> type; };

typedef mpl::apply<make_vector<_>, T>::type vector_of_t;

typedef mpl::apply<std::vector<_>, T>::type vector_of_t;

3.5.4 “懒惰”的重要性

struct always_int

{

typedef int type;

};

{

template <class T>

};

typedef mpl::vector<int, char*, double&> seq;

3.6 细节

MPL

#include <boost/mpl/component-name.hpp>

lambda表达式

lambda表达式的一种。通过使用占位符达到部分函数应用复合元函数的目的。正如你将会在本书中随处可见的，这些特性给予我们惊人的能力，允许我们从原始的元函数构造出几乎任意复杂的类型计算——就在它被使用之处：

// find the position of a type x in some_sequence such that:

//         x is convertible to 'int'

//      && x is not 'char'

//      && x is not a floating type

typedef mpl::find_if<

some_sequence

, mpl::and_<

boost::is_convertible<_1,int>

, mpl::not_<boost::is_same<_1,char> >

, mpl::not_<boost::is_float<_1> >

>

>::type iter;

lambda元函数（The ‘lambda’ metafunction

lambda表达式转化为元函数类的元函数。要得到关于lambdalambda求值过程的更为详细的信息，请参考MPL的参考手册。

apply元函数（The ‘apply’ metafunction

Boost库中，最难研究的就是MPLPreprocesser Lib了，现在大师为我们高屋建瓴的描述了其设计理念，使我们不至于被重重细节蒙住眼睛。

所以，个人觉得翻译这活，中文水平最重要。要把话说“顺溜”了可真不是件容易事儿。

[1] 译注：MPLBoost库里面的一个子库。用于支持模板元编程。下文会多次提到这个MPL库。

[2] 译注：这里原文为Dimensional Analysis，这里的Dimensional并非作通常意义上的维度解释。而是作为物理上的单位解释，因为下文讲的正是如何在编译期对物理量进行单位检查，进而实现一个编译期的健全的单位系统。Dimensional Analysis的正式称呼为“量纲分析”，太学术化，所以这里我们用通常物理上的称呼。

[3] 1/x看成x的－1次方。由此，m/s2可以写成ms-2，就由商的形式变成了积的形式。

[4] 译注：作者的意思是“让每个不同的单位成为不同的类型”。

[5] 译注：这里的原文是“...represent numbers”，直译为“...表示数值”，但这里的意思其实是表示数值的单位。

[6] namespace alias=namespace-name;alias声明为namespace-name的别名。在本书的许多例子中都会使用mpl::来表示boost::mpl::

[7] 译注：元函数本身是个类模板。而元函数类是个类型，它将元函数内嵌为一个名为apply的类模板，这两个称呼在后面将会多次提到，请读者注意它们的区别。

[9] 译注：不过似乎boost 1.31.0 里面的mpl::apply并没有这个特性。或许是权衡后的考虑？

[10] MPL参考手册的Configuration Macros部分描述了如何改变mpl::apply能够接受的参数个数的上限。

[11] 译注：这里作者的原文是...in the world of functional programming...”，本该译为“...在函数式编程中...”，然而考虑到“函数式编程”可能会发生误导，而译为“在函数式编程语言中”则不会，因为后者是个被广泛使用的名词。

[12] 译注：这里还可以译为“元函数组合”“元函数合成”等，视composition的译法而定。但考虑到数学中的“复合函数”一说，所以这里译为“复合元函数”，“复合”可作动词，可作形容词。如果作动词则表示“将元函数复合起来”，这正是原文表达的意思，如果作形容词则表示“复合后的元函数”，这是“复合”的结果。这样似乎更好一些:)

[13] 译注：这里所说的lambda表达式的各个参数并非该lambda接受的“外界”参数，举个例子：mul<plus<_1,_2>,minus<_1,_2> >这个lambda表达式的参数就是 plus<_1,_2>minus<_1,_2>而它们各自又都是lambda表达式，所以它们会先被求值，然后将结果传给mul

[14] 译注：事实上，lambda表达式的求值是个递归的过程。

[15] MPL缺省提供了5个占位符。MPL参考手册的Configuration Macros部分有关于如何改变提供的占位符的数目的描述。

[16] 译注：如果该占位符为_N，那么就会返回实际参数中的第N个参数，占位符的“占位”的意思就是：_N“占”的是第N个参数的位置。

[17] 译注：lazy evaluation的意思是“不到必要时不求值”。

[18] 译注：这里，“命名(naming)”的意思是，仅仅给它一个名字（意味着“仅仅实例化它的名字”），而并不对该计算求值（意味着“并不实例化该类”）。

[19] 译注：一个不错的例子是apply_if，其详细介绍见boost的官方文档。

[20] 译注：这里的原文写得相当拗口，所以译文遵循前文的定义。含义一样。

#### 评论

#  回复：《C++ Template Metaprogramming》第三章 深度探索元函数 2004-09-01 8:34 AM 赵忠祥

#  回复：《C++ Template Metaprogramming》第三章 深度探索元函数 2004-09-01 9:44 AM firingme

#  回复：《C++ Template Metaprogramming》第三章 深度探索元函数 2004-09-01 9:56 AM 曾毅

btw:刘兄，你上次给我的文章再给我一下，我要发布到CSTC上去。

#  回复：《C++ Template Metaprogramming》第三章 深度探索元函数 2004-09-01 5:01 PM 刘未鹏

to fireingme:

BTW.英文版下载地址参看BoostConsult网站。我也望了，搜一下吧

#  回复：《C++ Template Metaprogramming》第三章 深度探索元函数 2004-09-02 9:43 AM firingme

#  回复：《C++ Template Metaprogramming》第三章 深度探索元函数 2004-09-02 3:18 PM 刘未鹏

#  回复：《C++ Template Metaprogramming》第三章 深度探索元函数 2004-09-03 6:16 PM firingme

#  写在C 图书出版史上又一部经典著作问世之前 2004-09-03 7:49 PM AB
Ping Back来自：blog.csdn.net
• 0
点赞
• 0
收藏
觉得还不错? 一键收藏
• 0
评论
09-01 81
06-27 294
01-13 3万+
11-05
07-09
10-19
02-27 499
03-02 340
02-27 327
02-25 1220
02-28 406
02-27 179

### “相关推荐”对你有帮助么？

• 非常没帮助
• 没帮助
• 一般
• 有帮助
• 非常有帮助

1.余额是钱包充值的虚拟货币，按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载，可以购买VIP、付费专栏及课程。