C++ Metaprogramming 和 Boost MPL (下)

来源:互联网 发布:霍斯星战役 知乎 编辑:程序博客网 时间:2024/05/21 06:43

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


本文转自 http://kuibyshev.bokee.com/1585123.html


(四)MPL的迭代器

1.迭代器的定义和分类

      迭代器是一类指向某一个或一定范围序列元素的实体,在MPL中起着解耦算法和序列关系的作用,由于这个作用,MPL中的算法甚至可以用于任何编译时的类型,只要这种类型符合迭代器的要求就可以了。

      MPL中的迭代器一共分为3种:前向迭代器(Forward Iterator)、双向迭代器(Bidirectional Iterator)和随机访问迭代器( Random Access Iterator)。按照定义,这3种迭代器按照从先到后的顺序访问能力逐步增强。前向迭代器拥有产生指向到下一个元素的迭代器的能力,双向迭代器比起前向迭代器增加了产生指向前一个元素的迭代器的能力,随机访问迭代器则在双向迭代器的基础上能够根据特定的整数值产生同一序列中相对位置的迭代器。序列中也有3种访问方式的分类(前向、双向、随机访问),实际上序列的访问方式正是根据它可以产生的迭代器能力大小来分类的。

      在目前的MPL实现中,随机访问迭代器与双向迭代器实际上是等价的,因为有且只有vectorrange_c同时支持这两个迭代器。

 

2.迭代器的操作

      迭代器支持以下6个操作:

advance

把迭代器移动一个相对的位置N。双向迭代器中N可为负值。

distance

计算出两个迭代器之间的距离,并包装在整型常量类中返回。

next

返回序列下一个迭代器,取决于序列的定义。

prior

返回序列上一个迭代器,取决于序列的定义。

deref

解引用,也就是提取迭代器指向的类型。

iterator_category

返回一个迭代器分类标记类型。

3.源代码分析

      于每一个迭代器的操作包括了好几个实现版本,本文将不逐一分析每个实现,仅以advanceprior为例分析迭代器的一些基本实现手段。首先观察vector中的prior的源代码:

template<      typename Vector    , long, n_    >struct prior< v_iter<Vector,n_> >{    typedef v_iter<Vector,(n_ - 1)> type;};

      现在这个通用接口又把具体的实现交还给了序列的迭代器v_iter,这段代码充分体现了随机访问迭代器的特点。迭代器v_iter中有这样定义:

    typedef typename v_at<Vector,n_>::type type;

      这个绣球抛到内部函数v_at那里去了,那是实现类型检索的最核心部件。

template< typename Vector, long n_ >struct v_at    : aux::wrapped_type< typename v_at_impl<Vector,n_>::type >{}; template< typename Vector, long n_ >struct v_at_impl{    typedef long_< (Vector::lower_bound_::value + n_) > index_;    typedef __typeof__( Vector::item_(index_()) ) type;};

      上文在分析v_item时曾经提到过一个静态的成员函数item_,其特别之处在于index_是一个整型常量类,函数item_在以v_item为桥梁的连串的继承(从vector0继承到vectorn)中不断被重载,于是,对于vectorn中的某个索引值,只要调用item_,编译器就可以知道被重载的相应的类型值。最为巧妙的是,函数item_完全不需要定义,它仅仅在编译期用于快速索引vector中的一个类型。代码中似乎新建了一个index_对象,但其实这个对象在运行期不会产生任何作用,因为__typeof__只运行在编译期。

      现在来分析advance的实现,它的底层需要以priornext为基础以移动迭代器:

//外层通用接口,参见下一节“标记分派元函数”的说明template<      typename BOOST_MPL_AUX_NA_PARAM(Iterator)    , typename BOOST_MPL_AUX_NA_PARAM(N)    >struct advance    //根据tag标记来选择相应迭代器的实现版本: advance_impl< typename tag<Iterator>::type >        ::template apply<Iterator,N>{}; template< typename Tag >struct advance_impl{//Iterator为要执行操作的迭代器,N是偏移值,可能为负template< typename Iterator, typename N >struct apply    {        //如果N小于0,则需要向反方向移动迭代器,//less见下文分析typedef typename less< N,long_<0> >::type backward_;//保证偏移值为正值,if_的原理上文已分析过        typedef typename if_< backward_, negate<N>, N >::type offset_;       //根据是否反方向移动,执行辅助元函数,见下面的代码//此处代码已展开宏以方便阅读        typedef typename if_<              backward_            , aux::advance_backward< offset_::value >            , aux::advance_forward< offset_::value >            >::type f_;         typedef typename apply_wrap1<f_,Iterator>::type type;    };};      apply_wrapn是一个元函数的包装器,只是为了用更短的写法调用一个元函数类。template<      typename F, typename T1  >struct apply_wrap1     : F::template apply<T1>{};

      advance_backward利用prior逐位反向移动,直到递归到特化版本为止,当中利用了一种称为“解循环/递归”的特殊方法,目的是用预定义的大块移动减少循环次数,提高执行效率。

//特化版本,包括了从0到4共五个template< long N > struct advance_backward;template<>struct advance_backward<0>{    template< typename Iterator > struct apply    {        typedef Iterator iter0;        typedef iter0 type;    };}; template<>struct advance_backward<1>{    template< typename Iterator > struct apply    {        typedef Iterator iter0;        typedef typename prior<iter0>::type iter1;        typedef iter1 type;    };};......//非特化版本,当N大于4时调用template< long N >struct advance_backward{    template< typename Iterator > struct apply    {          //利用上述特化的最大值进行大块(chunk)移动,         //使之尽快接近目的地typedef typename apply_wrap1<              advance_backward<4>            , Iterator            >::type chunk_result_;         typedef typename apply_wrap1<              advance_backward<(                (N - 4) < 0                    ? 0: N - 4)>            , chunk_result_            >::type type;    };};

      advance_forward做法类似,在此不再列举了。

 

(五)MPL序列的内部元函数

1.元函数和元函数类

      上文提到的所有元函数,本质上都是一个类模板。上文分析过,这种简单的做法实际上隐藏着一个问题,由于C++严格区分类和模板,迫使模板无法直接传入另一个模板作为类型参数,除非使用限制比较大的“类模板的模板参数”。MPL的作者巧妙地解决了这个问题,方法是使用元函数类(Metafunction Class)。一个元函数类总是用一个类来包装一个命名为apply的模板,而这个apply模板中必须包括一个返回值type,代码类似于:

struct function_name{       template <typename t1, typename t2,...>       struct apply       {              //要执行的操作            typedef ... type;       };};

      元函数类的好处是有效地包装了元函数的功能,使得元函数可以像一个普通类一样使用,同时规定了::apply<>::type风格的统一接口,使得元函数类很容易进行组合,与STL的仿函数用法十分相近。

 

2.序列内部元函数的机制

      序列内部的元函数指的就是上文提及的序列支持的各类操作,这些操作在名称上完全仿照STL,但MPLSTL的差别在于,STL容器的操作通常是作为容器成员函数定义和使用的。在MPL中,这些操作定义在序列的外部,无论使用任何一个操作都必须包含一个声明这个操作的头文件,比如要使用at这个元函数,就必须包含at.hpp文件。

      使用通用的接口必然要求对类型进行识别,不幸的是,类模板无法(联系到不定序列的组织形式,或者也可以说是不便)提供像C++类成员函数那样的重载和匹配参数的能力,为了使用通用的接口,唯一的方案就是用一些预定义的标记来识别,在MPL中,每个标记实际上都是一个类。利用这种机制实现的元函数称作标记分派元函数(Tag Dispatched Metafunction)。

      每一个标记分派元函数总是包含3个部分:一个元函数(作为外部的通用接口)、一个相关联的产生标记的元函数(区分实体类型)和底层实现(以元函数类的形式出现)。通过标记的判断,外层结构选择相应的底层实现来执行具体的操作,达到一种重载的效果。

      MPL中大多数与序列和迭代器打交道的元函数都遵循标记分派元函数的机制来实现。

 

3.源代码分析

      由于序列的元函数为数甚多,每一个元函数又包括了好几个实现版本,本文将不逐一分析每一个实现,仅挑选具有代表性的元函数进行分析,以了解它们的概貌和内在的关系。

      序列的内部元函数与迭代器关系十分密切,常常相互作用,以vector为例,元函数begin的接口和实现代码分别是:

template<      typename Sequence=na    >struct begin{    typedef typename sequence_tag<Sequence>::type tag_;    typedef typename begin_impl< tag_ >        ::template apply< Sequence >::type type; }; template<>struct begin_impl< aux::vector_tag >{    template< typename Vector > struct apply    {        typedef v_iter<Vector,0> type;    };};


      这段代码非常典型,首先它体现出标记分派机制的作用,模板的特化能力令编译器能够找到适合的begin_impl版本;另一方面,它又是元函数类的一个典型例子。可以看到,begin返回了一个指向vector头部的迭代器,然而从上文的分析又可以知道,v_iter内部实际上还是调用了at的底层实现。

      对序列进行删除的操作时会使用到vector的另外一个底层结构v_mask

template<      typename Base    , int at_front    >struct v_mask    : Base{    typedef typename prior<typename Base::upper_bound_>::type index_;    typedef index_ upper_bound_;    typedef typename prior<typename Base::size>::type size;    typedef Base base;     static aux::type_wrapper<void_> item_(index_);    using Base::item_;}; template<      typename Base    >struct v_mask<Base,1>    : Base{    typedef typename Base::lower_bound_ index_;    typedef typename next<index_>::type lower_bound_;    typedef typename prior<typename Base::size>::type size;    typedef Base base;     static aux::type_wrapper<void_> item_(index_);    using Base::item_;};


      其定义与v_item非常相似,当需要用pop_front进行头部的删除时,v_mask便会被使用了:

template<>struct pop_front_impl< aux::vector_tag >{    template< typename Vector > struct apply    {        typedef v_mask<Vector,1> type;    };};


      pop_front调用的是v_mask的特化版本,当v_mask的第二个模板参数被设定为1时,表示从头部删除,否则将表示从尾部删除。事实上v_mask并没有真正删除一个类型,它所做的事情只是屏蔽了第一个或最后一个元素。类似地pop_back也使用v_mask,差异仅仅是第二个参数改为0而已。

      push_frontpush_back使用的仍然是vector的最基础设施v_item,其桥梁作用使得在头尾插入异常简单:

template<>struct push_front_impl< aux::vector_tag >{template< typename Vector, typename T >struct apply    {        typedef v_item<T,Vector,1> type;    };};


      相应地,push_back的实现中需要把v_item的第三个参数改为0



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


本文转自 http://kuibyshev.bokee.com/1585253.html


(六)MPL中的元函数

1.元函数的分类

      与上述的序列内部元函数不同,这里指的元函数是具有通用意义的一些独立于数据结构的元函数,更确切地,可以与STL中的仿函数相对应。在运行时的编程中,普通的函数与重载了()运算符的仿函数类是截然不同的,但在模板元编程中,尽管就接口和用法而言,有普通的元函数和元函数类的区别,但模板元编程的本质上来说,两者并无区别。MPL的作者把一些在STL仿函数中的对应物抽取出来分为一类,仍以“元函数(Metafunctions)”称呼;而服务于序列和迭代器元函数则称为“内部元函数(Intrinsic Metafunctions)”;另有一类通用元函数,称为“算法(Algorithms)”。这也就是名词上容易造成一些混淆的原因。

      MPL中的元函数分为两大类:通用元函数和数值元函数。通用元函数的作用类似于STL的仿函数配接器(Function Adapter),包括了类型选择(以if_为代表)以及元函数的调用、组合和绑定设施(以Lambda表达式为代表)。数值元函数则差不多与STL的仿函数相对应,包括了算术、比较、逻辑和位运算四类。

2.元函数的机制

      首先来看看数值元函数的源代码,以less<>元函数为例:

//下面代码已展开所有宏template< typename N1=na, typename N2=na >struct less: less_impl<         //less_tag是一个简单的类型包装,定义见下          typename less_tag::type        , typename less_tag::type        >::template apply::type{};//less_tag用于取得参数的标记,起识别运算符的作用template< typename T > struct less_tag{    typedef typename T::tag type;};//如果两个参数都由标记判断出是整型常量类,则使用以下特化版本template<>struct less_impl< integral_c_tag,integral_c_tag >{    template< typename N1, typename N2 > struct apply        : bool_< (N2::value > N1::value ) >    {    };};


      less的原理很简单,核心部分只是判断两个参数的value成员的大小而已。其他的数值元函数原理也一样。

      至于通用元函数的机理,其核心是Lambda表达式的调用和组合能力,集中在下一节进行叙述。

(七)Lambda表达式

1lambda演算

      逻辑学家Alonzo Church1957年发明了称为lambda演算的数学体系。这种体系可以作为函数式编程语言的模型使用,类似于图灵机可以用于命令式的编程语言模型。实际上,lambda演算作为计算的描述与图灵机是相当的。

      lambda演算的基础是lambda抽象:

            λx.+1 x

      其含义是建立了一个未命名的函数,此函数接受一个x作为参数,要实现1+x的操作。在lambde演算中,总是使用前缀表达式。如果需要应用这个lambda抽象,则写作:

            λx.+1 x5

      这表示把1+x应用在常量5上。lambda演算使用归约规则应用函数,并产生结果。在这里,归约规则将用5来替换lambda抽象中的x,然后去掉lambda符号,得到表达式(+1 5)即3

2MPL中的Lambda

      模板元编程既然是函数式风格的编程,自然应当允许执行lambda演算。MPL就提供了这样的设施,称之为Lambda表达式。每一个Lambda表达式都是一个编译期可调用的实体,它包含两种形式:元函数类和占位符表达式(Placeholder Expression)。

      MPL把占位符表达式定义为:一种placeholder类或者一个包含至少一个占位符表达式参数的类模板的特化。所有的placeholder类都以“_”开头,例如__1_2、……_n。这种写法只是为了方便使用而已,其背后实际上是一个类模板arg<n>,定义如下:

//已展开所有宏,n的范围从1到BOOST_MPL_LIMIT_METAFUNCTION_ARITYtemplate<> struct arg<n>{    static const int value  = n;    typedef arg<n+1> next;    typedef na tag;    typedef na type;    template<          typename U1 = na, typename U2 = na, typename U3 = na        , typename U4 = na, typename U5 = na        >    struct apply    {        typedef Un type;        //如果type是na,则表明未传递足够的参数,//需要产生一个错误信息BOOST_MPL_AUX_ASSERT_NOT_NA(type);    };};

      arg<n>::apply允许接受的参数个数是由BOOST_MPL_LIMIT_METAFUNCTION_ARITY来决定的,MPL默认的设置是5,即最多接受5个参数。MPL还另外特化了一个arg<-1>,这个类型代表无参数的含义。

      占位符的常用形式由arg<n>而来:

typedef arg< -1 > _;typedef arg<1> _1;typedef arg<2> _2;......

      可见,占位符实质是元函数类,它们能够起到选择参数表中特定位置的参数的作用,并把决定参数的时间推迟到向占位符填充实际的类型的时候。比如_2::apply<a1, an>::type即表示参数表中的第二个参数的类型值。有了这些占位符,lambda演算中的变量就可以用它做中介,在使用Lambda表达式时,为被组合的元函数选择参数了。这样,元函数就可以作为一类值优雅地组合在一起,请看下面这个例子:

typedef plus<_, int_<2> >expr;typedef lambda::type func;

      这段代码把一个元函数包装起来,生成一个接受一个参数的lambda表达式,作用是为一个常量包装类的值加上2int_<2>。这中间的巧妙之处就是,plus元函数接受两个类型参数,普通的整型常量类是类,占位符同样是类,它们都作为实际参数传入,并没有任何差别。特别地,占位符在进行了Lambda表达式绑定以后,能够自动地对参数表进行选择,把正确的参数取来作为“真正的”实际参数,当中的过程十分微妙。

      lambda元函数的定义如下:

template<      typename T = na    , typename Tag = void_,      typename Arity = int_< aux::template_arity::value >            >struct lambda;

      它有许多个不同的特化版本,为不同的参数进行特别处理。下面这段代码是为拥有一个参数的类模板而特化的,这说明普通的元函数也可以用于Lambda表达式。

template<      template< typename P1 > class F    , typename T1    , typename Tag    >struct lambda<f, Tag >{    typedef lambda< T1,Tag > l1;    typedef typename l1::is_le is_le1;    typedef typename aux::lambda_or<          is_le1::value        >::type is_le;    typedef aux::le_result1<          is_le, Tag, F, l1        > le_result_;    typedef typename le_result_::result_ result_;    typedef typename le_result_::type type;};......

      对于普通的元函数类,特化版本是这样的:

template<      typename F, typename T1    , typename Tag    >struct lambda<          bind1< F,T1 >, Tag, int_<2>        >{    typedef false_ is_le;    typedef bind1 result_;    typedef result_ type;};

      从上面的代码可以知道,lambda的转换操作最终都要使用到bindn元函数类,这个函数正是高阶函数式编程的关键。这里仍以bind1为例:

template<      typename F, typename T1    >struct bind1{    template<          typename U1 = na, typename U2 = na,typename U3 = na, typename U4 = na,typename U5 = na        >    struct apply    {     private:        //遇到_或arg<-1>占位符,根据其出现的//位置来解析为相应的arg,这里只绑定一个参数,//所以占位符要解析为arg<1>typedef aux::replace_unnamed_arg< F, mpl::arg<1> > r0;        typedef typename r0::type a0;        typedef typename r0::next n1;//f_在这里实际上就是要绑定的元函数类F        typedef typename aux::resolve_bind_arg< a0,U1,U2,U3,U4,U5 >::type f_;               typedef aux::replace_unnamed_arg< T1,n1 > r1;        typedef typename r1::type a1;        typedef typename r1::next n2;//当resolve_bind_arg的第一个参数为arg时,// t1::type的运算结果将是Un,即选择传入的//参数列表中的第n个作为元函数类F的参数,//比如在bind1这里,结果将是U1。        typedef aux::resolve_bind_arg< a1,U1,U2,U3,U4,U5 > t1;     public://apply_wrapn的作用其实是为接受多个参数的元函数类f_//生成一个新的元函数类,它只接受一个类型参数//以vector的形式出现,其中an是//f_原来的参数。        typedef typename apply_wrap1<              f_            , typename t1::type            >::type type;    };};

      上面的源代码逐段看起来还很令人疑惑,概括起来

typedef bindn g;

      的含义相当于

struct g{    template<          typename U1 = unspecified        ...        , typename Un = unspecified        >    struct apply        : apply_wrapn<              typename h0::type            , typename h1::type            ...            , typename hn::type            >    {    };};

      其中hk就相当于上面代码中的resolve_bind_arg,遇到元函数类时type就是该元函数类,遇到arg<n>type就是参数Un所以,元函数类bindn能够将元函数绑定在第n个占位符,从而生成接受n个参数的更高阶的元函数类。Lambda表达式也就是这样被构造出来的。

(八)MPL中的算法

      MPL的提供了相当丰富的算法,这些算法的概念基本上跟STLalgorithm头文件中定义的算法相对应,熟悉STL的程序员能够很轻易地掌握它们的用法。有的算法依赖于序列的类型,不同的序列会定义自己不同的实现方法。有的算法则用迭代器和MPL的一些元函数实现出来。

      其中的iter_fold元函数类,作用是把一个操作运用在序列中的每一个类型上。iter_foldMPL的算法实现中起着关键作用:

template<     //欲作用的序列      typename Sequence=na     //开始状态,比如定义为序列的开端begin, typename State=na//对序列中的每个元素进行的操作    , typename ForwardOp=na    >struct iter_fold{    typedef typename aux::iter_fold_impl<          ::boost::mpl::O1_size::value        , typename begin::type        , typename end::type        , State        , typename lambda::type        >::state type;   };template<      typename First    , typename Last    , typename State    , typename ForwardOp    >struct iter_fold_impl< n,First,Last,State,ForwardOp >{    typedef First iter0;    typedef State state0;    typedef typename apply2< ForwardOp,state0,iter0 >::type state1;    typedef typename mpl::next::type iter1;    typedef typename apply2< ForwardOp,state1,iter1 >::type state2;    typedef typename mpl::next::type iter2;    ......//逐次迭代,直到把序列中的所有元素都执行了一次ForwardOp。    typedef staten state;    typedef itern iterator;};

      比如当我们要构造一个返回序列numbers中最大元素的元函数类,就可以这样利用iter_fold

typedef iter_fold<      numbers    , begin::type    , if_< less< deref<_1>, deref<_2> >,_2,_1 >    >::type max_element_iter;

      由此也可见到其他算法对iter_fold(包括其他的迭代算法比如foldreverse_foldreverse_iter_fold等)的依赖。这种依赖是基础性的,为什么呢?对比起STL的迭代器,模板元编程本身实际上无法实现真正的迭代器访问,比如没有方便的“++”运算,同时在模板元编程中,由于无法定义变量,保存运算中的状态需要相当的笨拙的方法(如上面的iter_fold)。如果没有这些基础的遍历算法,其他算法就会需要大量重复冗长的代码来完成功能了。

      这里就不一一赘述其他的算法了,参考着STL的概念,是容易理解它们的实现的,不过如上分析,因为这是模板元编程的缘故,有许多代码不像普通程序实现得那么直接。





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


本文转自 http://kuibyshev.bokee.com/1585305.html


结论

 

(一)为什么要使用模板元编程?

      一项新的技术的引入,如果不是为了取代旧有的技术,那么必然是可以加强旧有的技术。C++的发展史中有无数例子阐述了这一点。模板元编程是一种编译时的计算,它不会也不可能取代运行时必须的动态处理技术。然而通过巧妙的模板元编程,一些传统上存在的技术矛盾得以缓解,程序能够以更优雅更自动的方式组织起来。

      第一、一些原来要留到运行时才能确定的类型或数据可以提前到编译时确定,提高了程序的效率。

      第二、程序有时可以当成元数据输入元函数处理,依赖模板元编程的代码生成能力,自动产生代码,在熟练掌握的情况下,编程效率得到提高,并且这些代码往往还带有某种规范性,容易管理。

      第三、在C++中,模板元编程所用的语言是其子集,程序员无需额外学习别的元语言来操纵程序,他们可以用很自然的方式去理解和运用模板元编程。

 

(二)为什么要使用MPL

      在接触MPL之前,人们确实容易疑惑,像模板元编程这样应用面较狭窄的高端技术,为什么还需要建立一个库呢?只有通过对MPL的了解和分析,MPL的强大威力才能展现在我们眼前。MPL的最大意义在于,它重新整合了先前零散发展起来的模板元编程技术,建立了一套相当完整的标准,使模板元编程从深奥难用的理论出发走向了实用化的道路。

      概括起来,至少有四个原因使用MPL[6]

      1.  质量。MPL无疑是一个高质量的通用程序库,无论从架构到代码的实现,都体现出工业的强度,其代码的准确性和高效性更是完全值得信赖,用侯捷的话来说就是“无所不用其极”[24]

      2.  重用性。MPL的重用性突出表现在它拥有一个概念完整、耦合度低的组织架构,使模板元编程的一般使用者可以轻易摆脱一些复杂但与问题域无关的技术考虑,可以集中精力进行关于问题域的设计。

      3.  可移植性。正如上文的分析,MPL为了跨越平台和编译器做了极多的基础工作,能够在各大主流编译器上成功编译。单是这一点已经可以作为使用MPL的充分理由了。假如没有这个库的包装,很难想象要为那些没有模板局部特化能力的编译器额外写多少代码,也更难想象调试和优化在存在bug的编译器上是如何困难。

      4.  乐趣!MPL能把程序员从大量重复性的烦杂劳动中解脱出来,要写出更健壮的程序成为了更加容易的事情。

      MPL作为模板元编程发展史上的里程碑,必将为这个领域的发展起到引导作用。审慎乐观地估计,模板元编程终将在不远的将来走下金字塔的尖端,成为C++程序员日常编程工作的一部分。


参考书目

 

[1]      Accelerated C++》,Andrew KoenigBarbara Moo著,覃剑锋、柯晓江、蓝图译,中国电力出版社2003年版

[2]      Algorithm Specialization in C++ Using Template Metaprograms》,Todd Veldhuizenhttp://osl.iu.edu/~tveldhui/papers/Metaprogram-Slides.ps

[3]      Blitz++ Libraryhttp://www.oonumerics.org/blitz/

[4]      Boost Configuration Libraryhttp://www.boost.org/libs/config/index.html

[5]      Boost Lambda Libraryhttp://www.boost.org/libs/lambda/index.html

[6]      Boost MPL Libraryhttp://www.boost.org/libs/mpl/doc/index.html

[7]      Boost Preprocessor Libraryhttp://www.boost.org/libs/preprocessor/doc/index.html

[8]      Boost Static Assert Libraryhttp://www.boost.org/libs/static_assert/static_assert.htm

[9]      Boost Type Traits Libraryhttp://www.boost.org/libs/type_traits/index.html

[10]  Boost源码剖析之:型别分类器——type_traits》,刘未鹏,http://blog.csdn.net/pongba/archive/2004/08/24/83828.aspx

[11]  Boost源码剖析之:增强的std::pair——Tuple Types》,刘未鹏,http://blog.csdn.net/pongba/archive/2004/08/24/83813.aspx

[12]  C++ Template Metaprogramming: Concepts, Tools, and Techniques from Boost and Beyond》,第三章,David AbrahamsAleksey Gurtovoy著,http://www.awprofessional.com/titles/0321227255

[13]  C++ Templates: The Complete Guide》,David VandevoordeNicolai M. Josuttis著,陈伟柱译,人民邮电出版社2004年版

[14]  C维视点,http://www.c-view.org/

[15]  Data Structures and the Standard Template Library》,William J. Collins著,周翔译,机械工业出版社2004年版

[16]  Expression Templates》,Todd VeldhuizenC++ Report Magazine, Vol 7 No. 5, June 1995

[17]  Generative Programming Methods, Tools, and Applications》,Krzysztof Czarnecki,Ulrich W.Eisenecker著,Addison Wesley 1999

[18]  Modern C++ Design: Generic Programming and Design Patterns Applied》,Andrei Alexandrescu著,侯捷、於春景译,华中科技大学出版社2003年版

[19]  Practical C++ Programming, Second Edition》,Steve Oualline著,鄢爱兰 周丽琴译,中国电力出版社2004年版

[20]  Programming Languages: Principles and Practice, Second Edition》,Kenneth C. Louden著,电子工业出版社2003年版

[21]  Ruminations on C++: A Decade of Programming Insight and Experience》,Andrew KoenigBarbara Moo著,黄晓春译,人民邮电出版社2002年版

[22]  Scientific Computing: C++ versus Fortran》,Todd VeldhuizenDr. Dobb's Journal November 1997

[23]  Standard Template Library Programmer's Guidehttp://www.sgi.com/tech/stl/

[24]  STL源码剖析》,侯捷著,华中科技大学出版社2002年版

[25]  The C++ Programming Language, Third Edition》,Bjarne Stroustrup著,Addison-Wesley Publishing Company 1997年版

[26]  Thinking in C++, Second Edition, Volume 1 & 2》,Bruce Eckel著,http://www.bruceeckel.com/

[27]  Using C++ Template Metaprograms》,Todd VeldhuizenC++ Report Magazine, Vol 7 No. 4, May 1995

[28]  Wikipediahttp://www.wikipedia.org

[29]《即将问世的经典〈C++ Template Metaprogramming〉开放章节翻译及介绍》,刘未鹏,http://blog.csdn.net/pongba/category/39865.aspx









原创粉丝点击