读书笔记:STL源码剖析的再剖析 – traits

读书笔记:STL源码剖析的再剖析 – traits

[原创文章欢迎转载,但请保留作者信息]

Justin 于 2009-12-16

这一部分其实是书中的第三章,坦白说看之前我知道什么是iterator,但是traits却是从没听过。抓紧学习啊,很重要。
所以虽然第三章标题包含了迭代器和traits,这篇笔记只侧重于traits的记录。

在简要说明迭代器(iterator)是一种智能指针(smart pointer)之后,侯捷开始介绍traits技法。(我想可以翻译为特征引导,当然他老人家也说过了,简单的术语宁可不翻译)

  • 迭代器的相应类别(iterator associated types):我的理解是,在模板列表中添加一个模板变量,用来表明列表中某个迭代器的类别。
    就像简历里姓名之后又要带个性别来说明此人的男/女一样。下有代码一段,高亮处为iterator associated types的应用:
    template  < class  I,  class  T >             // -------------
    void  _func(I anIterator, T aType)      //  Level 1: this is where
    {                                          //  we see the type of the iterator
       aType TempVariable;                 //  and do something
       
    //  do whatever you like here
    }


    template 
    < class  I >                      // -------------
    inline func(I anIterator)                //  Level 2: this is where
    {                                       //  we extract the type, which
       _func(anIterator,  * anIterator);    //  frees the user from doing this
    }


    int  main( void )
    {
       
    int  i  =   7 ;
       func(
    & i);                           // --------------
       
    //  any other code          //  Level 3: this is how
    }
                                           //  user invokes our function
  • 模板偏特化(template partial specialization),不太喜欢侯捷翻译的这个术语,每次看到都有头疼的感觉。
    但是却也找不出个合适的词来代替,只能暂时理解为“通过限定模板函数的一部分参数类型来实现某种意义上的函数重载”,尽管这种“重载”的划分依据是函数所属的对象是通过不同的模板创建的。
    OK,为了方便肠道消化,上例子(出处):

    #include  < iostream >
    using   namespace  std;
     
    template
    < class  T,  class  U,  int  I >   struct  X
    {
       
    void  f()  { cout  <<   " Primary template "   <<  endl; }
    }
    ;
     
    template
    < class  T,  int  I >   struct  X < T, T * , I >

       
    void  f()  { cout  <<   " Partial specialization 1 "   <<  endl;}
    }
    ;
     
    template
    < class  T,  class  U,  int  I >   struct  X < T * , U, I >
    {
       
    void  f()  { cout  <<   " Partial specialization 2 "   <<  endl;}
    }
    ;
     
    template
    < class  T >   struct  X < int , T * 10 >
    {
       
    void  f()  { cout  <<   " Partial specialization 3 "   <<  endl;}
    }
    ;
     
    template
    < class  T,  class  U,  int  I >   struct  X < T, U * , I >
    {
       
    void  f()  { cout  <<   " Partial specialization 4 "   <<  endl;}
    }
    ;
     
    int  main()  {
       X
    < int int 10 >  a;
       X
    < int int * 5 >  b;
       X
    < int * float 10 >  c;
       X
    < int char * 10 >  d;
       X
    < float int * 10 >  e;
       a.f(); b.f(); c.f(); d.f(); e.f();
    }

    执行结果是:
       Primary template
       Partial specialization 1
       Partial specialization 2
       Partial specialization 3
       Partial specialization 4
    套用前面说的简历的例子:template partial specialization很贴心的提供了针对不同性别的不同的简历。填好名字之后,
       如果是男的,你就需要选择你是否考虑加入本地的足球队;
       如果是女的,可能会问你生理周期是多长;
       如果是春哥,也许就会要求提供原地满血复活的咒语@#¥%

    • 给自己的话:利用模板的参数推导机制,我们可以在模板空间实现函数的重载。
      但是需要知道的是每次模板的推导都伴随着一个临时对象的产生和销毁,这无形中就增加了代码的执行代价(overhead)。至于代价有多大,没有验证过……

  • 现在到了traits——类型提取。以下列出的是常用的(好吧我承认,是书中列出来的)迭代器相关的类型。由此我们可以得知有关迭代器的更多的信息。     
       iterator_category    -  用以提取迭代器的类别:InputIterator, OutputIterator, ForwardIterator, BidirectionalIterator, RandomAccessIterator
       value_type             -  迭代器所指对象的类别     
       difference_type       -  两个迭代器之间的距离,如果这两个迭代器分别指向容器的头和尾,就是该容器的大小     
       pointer                   -  返回指向对象的指针     
       reference               -  返回与对象相关联的引用
    要注意的有两点:
    一是我在这里用的一些词或术语并不忠实于原作,仅仅为了我自己理解方便(且避免头疼……);
    二是这些迭代器相关的类型是STL已经定义好了的。直接拿来用就可以了(通过继承iterator类就可以得到)。

  • SGI中有对STL的有益补充:__type_traits。
    引用侯捷语“iterator_traits负责萃取迭代器的特性,__type_traits则负责萃取型别(type)的特性”也就是说除了以上列出的关于迭代器的traits,SGI的STL还提供了对一般类型对象的traits,罗列如下:     
    has_trivial_default_constructor     
    has_trivial_copy_constructor     
    has_trivial_assignment_operator     
    has_trivial_destructor     
    is_POD_type
    以上五种traits,依具体对象的不同而定义(通过typedef)为__false_type或__true_type中的一个。
    前面四种都好理解,如果一个类里有指针成员并且需要对它们进行动态内存分配/释放等操作,那么这四个traits都应该是__false_type的:这表明对于该类的对象,constructor/copy constructor/assignment operator/destructor是non_trivial的。
    或者再直接点说,在这种情况下这些函数需要一些额外的操作(对于复制函数而言,这意味着要额外申请空间,然后再拷贝这些指针成员所指向的对象)
    第五种trait中的POD没看懂,也没在侯捷的说明里找到更多解释,只好再参考了这篇资料,顺带自我扫盲一下:(或许有疏漏谬误@#¥%)
    • POD就是Plain Old Data,指的是C与C++共同支持的数据类型(可以理解为交集)
    • 要一一记忆POD的类别太麻烦,脑子不够用。有个可供区分的特性,见下表:
      表达式 POD 类对象T 非POD 类对象 T
       new T 无初始化 按默认方式初始化
       new T() 总是按默认方式初始化
       new T(x) 总是通过构造函数初始化

      这样一来就看到一个区别:非POD对象总是会被初始化的,而POD对象并不能保证这一点。
    • 另外POD对象在layout、initialization、copying、addressing上也有自己的特点,此文不是要讨论POD,如果真的忘记了,就去看 这篇资料 吧。
其实说到底,traits就是对模板中的某个/几个参数进行限定并分类,由此实现的另一层意义上的重载。(个人理解,有待纠正或补充……)

你可能感兴趣的:(读书笔记:STL源码剖析的再剖析 – traits)