STL源码剖析——Iterators与Traits编程#5 __type_traits

  上节给出了iterator_traits以及用到traits机制的部分函数的完整代码,可以看到traits机制能够提取迭代器的特性从而调用不同的函数,实现效率的最大化。显然这么好的机制不应该仅局限于在STL里面使用,在前某一节中我们也有说到,traits机制能够萃取类的特性,而这个类分为两个类别,一是迭代器类,二普通类,迭代器类我们已经有所了解了,那么本节就来学习负责萃取普通类型特性的__type_traits吧。

  于普通类型而言,我们所关注的特性是指:这个类型是否具备non-trivial defalt ctor?是否具备non-trivial copy ctor?是否具备non-trivial assignment ctor?是否具备non-trivial dtor?在关注__typr_traits之前,我们很有必要来讨论一下上述的几个词组是什么意思。其中ctor为构造函数,那么copy ctor就是拷贝构造函数,assignment ctro为赋值构造函数,defalt ctor为默认构造函数。而trivial的意思为琐碎的、无意义的。那么判断这4个构造函数是否有意义的条件是:如果至少满足一下了3条中的1条,那么就是说明其类是有意义(non- trivial)的:

  1. 显式(explict)定义了这四种函数
  2. 类里有非静态非POD的数据成员
  3. 有基类

  何为POD?简单解释就是指C风格的struct结构体定义的数据结构或者C++的内建类型,POD类型必然有trivial ctor/dtor/copy/assignment四种函数。

  那么区分构造函数是否有意义又有什么意义呢?

  如果这个类都是trivial ctor/dtor/copy/assignment函数,我们对这个类进行构造、析构、拷贝和赋值时可以采用最有效率的方法,是不调用无所事事正真的那些ctor/dtor等,而是直接采用内存操作如malloc()、memcpy()等提高性能,获取最高效率。这对于大规模而操作频繁的容器,有着显著的效率提升。

  而这个__type_traits机制就能针对不同的类特性,在编译时期完成函数的调用决定。这对于template很有帮助,例如,当我们准备对一个类型特性未知的数组进行copy操作时,如果我们能事先知道其元素的类型特性是否有一个trivial copy ctor,便能够帮我们我们决定是否可使用快速的memcpy()或memmove()。

  与使用iterator_traits相似,我们可以这样运用__type_traits<T>:

1 __type_traits<T>::has_trivial_default_constructor
2 __type_traits<T>::has_trivial_copy_constructor
3 __type_traits<T>::has_trivial_assignment_operator
4 __type_traits<T>::has_trivial_destructor
5 __type_traits<T>::is_POD_type

  我们希望通过调用这些式子来告诉我们T类型是否是有意义的(以便我们决定采取什么策略),但其结果不应该只是个bool值,应该是个有着真/假性质的“对象”,因为我们希望利用其相应结果来进行参数推导(等会给出代码例子),而编译器只有面对class object形式的参数,才会做参数推导。为此,上述式子应该传回这样的东西:

1 struct __true_type {};
2 struct __false_type {};

  这两个空白的结构体没有任何东西,不会带来额外的负担,却又能标示真假,满足我们所需。

  为此,我们应该为上述的五个式子定义一些typedef,准确告知T是否有意义:

 1 template <class type>
 2 struct __type_traits {
 3     typedef __true_type     this_dummy_member_must_be_first;
 4     /*不要移除这个成员,它通知「有能力自动将 __type_traits特化」的编译器说,我们现在所看到的这个 __type_traits template 是特殊的。这是为了确保万一编译器也使用一个名为 __type_traits而其实与此处定义并无任何关联的template时,所有事情仍将顺利运作*/
 5     
 6     /* 以下条件应该被遵守,因为编译器有可能自动为各类型产生专属的 __type_traits特化版本:
 7     - 你可以重新排列以下的成员次序
 8     - 你可以移除以下任何成员
 9     - 绝对不可以将以下成员重新命名而却没有改变编译器中的对应名称
10     - 新加入的成员会被视为一般成员,除非你在编译器中加上适当支援
11     */
12     typedef __false_type    has_trivial_default_constructor;
13     typedef __false_type    has_trivial_copy_constructor;
14     typedef __false_type    has_trivial_assignment_operator;
15     typedef __false_type    has_trivial_destructor;
16     typedef __false_type    is_POD_type;
17     // 所谓 POD 意指 Plain Old Data structure. 
18 };

  this_dummy_member_must_be_first:因为某些编译器会提供__type_traits机制,自动会为所有类型提供适当的特化版本,所以为了将编译器内部的__type_traits和STL 自带的__type_traits区分开来,提供该特殊的定义式。

  为什么SGI把所有的内嵌类型都定义为__false_type呢?这是最保守的做法,它默认认为所有的自定义类型都是具有有意义的构造函数,至于内部基本类型,SGI STL会为其提供特化版本。总的来说,上述的__type_traits可以接受任何类型的参数,但五个typedef会经由以下的管道获得实值:

  • 自定义类型,内含对所有类型都必定有效的保守值。上述各个has_trivial_xxx型别都被定义为__false_type,就是对所有类型都必定有效的保守值。
  • 经过声明的特化版本,例如<type_traits.h>内对所有C++基本类型提供了对应的特化声明。稍后展示。
  • 某些编译器会自动为所有类型提供适当的特化版本。

  以下便是<type_traits.h>对所有C++基本类型所定义的__type_traits特化版本。这些定义对于内建有__type_traits支持能力的编译器并无伤害,对于无该支持能力的编译器而言,实属必要。

  1 /* 以下针对 C++ 基本型別 char, signed char, unsigned char, short, unsigned short, 
  2 int, unsigned int, long, unsigned long, float, double, long double 提供特化版本。
  3 注意,每一个成员的值都是 __true_type,表示这些型別都可采用最快速方式(例如 memcpy)
  4 來进行拷贝动作或赋值动作。*/
  5 
  6 //注意,SGI STL<stl_config.h>将以下出现的 _STL_TEMPLATE_NULL
  7 //定义为template<>,是所谓的class template explicit specialization
  8 
  9 __STL_TEMPLATE_NULL struct __type_traits<char> {
 10     typedef __true_type    has_trivial_default_constructor;
 11     typedef __true_type    has_trivial_copy_constructor;
 12     typedef __true_type    has_trivial_assignment_operator;
 13     typedef __true_type    has_trivial_destructor;
 14     typedef __true_type    is_POD_type;
 15 };
 16 
 17 __STL_TEMPLATE_NULL struct __type_traits<signed char> {
 18     typedef __true_type    has_trivial_default_constructor;
 19     typedef __true_type    has_trivial_copy_constructor;
 20     typedef __true_type    has_trivial_assignment_operator;
 21     typedef __true_type    has_trivial_destructor;
 22     typedef __true_type    is_POD_type;
 23 };
 24 
 25 __STL_TEMPLATE_NULL struct __type_traits<unsigned char> {
 26     typedef __true_type    has_trivial_default_constructor;
 27     typedef __true_type    has_trivial_copy_constructor;
 28     typedef __true_type    has_trivial_assignment_operator;
 29     typedef __true_type    has_trivial_destructor;
 30     typedef __true_type    is_POD_type;
 31 };
 32 
 33 __STL_TEMPLATE_NULL struct __type_traits<short> {
 34     typedef __true_type    has_trivial_default_constructor;
 35     typedef __true_type    has_trivial_copy_constructor;
 36     typedef __true_type    has_trivial_assignment_operator;
 37     typedef __true_type    has_trivial_destructor;
 38     typedef __true_type    is_POD_type;
 39 };
 40 
 41 __STL_TEMPLATE_NULL struct __type_traits<unsigned short> {
 42     typedef __true_type    has_trivial_default_constructor;
 43     typedef __true_type    has_trivial_copy_constructor;
 44     typedef __true_type    has_trivial_assignment_operator;
 45     typedef __true_type    has_trivial_destructor;
 46     typedef __true_type    is_POD_type;
 47 };
 48 
 49 __STL_TEMPLATE_NULL struct __type_traits<int> {
 50     typedef __true_type    has_trivial_default_constructor;
 51     typedef __true_type    has_trivial_copy_constructor;
 52     typedef __true_type    has_trivial_assignment_operator;
 53     typedef __true_type    has_trivial_destructor;
 54     typedef __true_type    is_POD_type;
 55 };
 56 
 57 __STL_TEMPLATE_NULL struct __type_traits<unsigned int> {
 58     typedef __true_type    has_trivial_default_constructor;
 59     typedef __true_type    has_trivial_copy_constructor;
 60     typedef __true_type    has_trivial_assignment_operator;
 61     typedef __true_type    has_trivial_destructor;
 62     typedef __true_type    is_POD_type;
 63 };
 64 
 65 __STL_TEMPLATE_NULL struct __type_traits<long> {
 66     typedef __true_type    has_trivial_default_constructor;
 67     typedef __true_type    has_trivial_copy_constructor;
 68     typedef __true_type    has_trivial_assignment_operator;
 69     typedef __true_type    has_trivial_destructor;
 70     typedef __true_type    is_POD_type;
 71 };
 72 
 73 __STL_TEMPLATE_NULL struct __type_traits<unsigned long> {
 74     typedef __true_type    has_trivial_default_constructor;
 75     typedef __true_type    has_trivial_copy_constructor;
 76     typedef __true_type    has_trivial_assignment_operator;
 77     typedef __true_type    has_trivial_destructor;
 78     typedef __true_type    is_POD_type;
 79 };
 80 
 81 __STL_TEMPLATE_NULL struct __type_traits<float> {
 82     typedef __true_type    has_trivial_default_constructor;
 83     typedef __true_type    has_trivial_copy_constructor;
 84     typedef __true_type    has_trivial_assignment_operator;
 85     typedef __true_type    has_trivial_destructor;
 86     typedef __true_type    is_POD_type;
 87 };
 88 
 89 __STL_TEMPLATE_NULL struct __type_traits<double> {
 90     typedef __true_type    has_trivial_default_constructor;
 91     typedef __true_type    has_trivial_copy_constructor;
 92     typedef __true_type    has_trivial_assignment_operator;
 93     typedef __true_type    has_trivial_destructor;
 94     typedef __true_type    is_POD_type;
 95 };
 96 
 97 __STL_TEMPLATE_NULL struct __type_traits<long double> {
 98     typedef __true_type    has_trivial_default_constructor;
 99     typedef __true_type    has_trivial_copy_constructor;
100     typedef __true_type    has_trivial_assignment_operator;
101     typedef __true_type    has_trivial_destructor;
102     typedef __true_type    is_POD_type;
103 };
104 
105 #ifdef __STL_CLASS_PARTIAL_SPECIALIZATION
106 
107 template <class T>
108 struct __type_traits<T*> {
109     typedef __true_type    has_trivial_default_constructor;
110     typedef __true_type    has_trivial_copy_constructor;
111     typedef __true_type    has_trivial_assignment_operator;
112     typedef __true_type    has_trivial_destructor;
113     typedef __true_type    is_POD_type;
114 };
115 
116 #else /* __STL_CLASS_PARTIAL_SPECIALIZATION */
117 
118 struct __type_traits<char*> {
119     typedef __true_type    has_trivial_default_constructor;
120     typedef __true_type    has_trivial_copy_constructor;
121     typedef __true_type    has_trivial_assignment_operator;
122     typedef __true_type    has_trivial_destructor;
123     typedef __true_type    is_POD_type;
124 };
125 
126 struct __type_traits<signed char*> {
127     typedef __true_type    has_trivial_default_constructor;
128     typedef __true_type    has_trivial_copy_constructor;
129     typedef __true_type    has_trivial_assignment_operator;
130     typedef __true_type    has_trivial_destructor;
131     typedef __true_type    is_POD_type;
132 };
133 
134 struct __type_traits<unsigned char*> {
135     typedef __true_type    has_trivial_default_constructor;
136     typedef __true_type    has_trivial_copy_constructor;
137     typedef __true_type    has_trivial_assignment_operator;
138     typedef __true_type    has_trivial_destructor;
139     typedef __true_type    is_POD_type;
140 };

  对于__type_traits的应用,我们可以举一个之前学习Allocator时就遇到的函数destroy():

 1 // 以下是 destroy() 第二版本,接受兩個迭代器。此函式是設法找出元素的數值型別,
 2 // 進而利用 __type_traits<> 求取最適當措施。
 3 template <class ForwardIterator>
 4 inline void destroy(ForwardIterator first, ForwardIterator last) {
 5   __destroy(first, last, value_type(first));
 6 }
 7 
 8 // 判斷元素的數值型別(value type)是否有 trivial destructor
 9 template <class ForwardIterator, class T>
10 inline void __destroy(ForwardIterator first, ForwardIterator last, T*) {
11   typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor;
12   __destroy_aux(first, last, trivial_destructor());
13 }
14 
15 // 如果元素的數值型別(value type)有 non-trivial destructor…
16 template <class ForwardIterator>
17 inline void
18 __destroy_aux(ForwardIterator first, ForwardIterator last, __false_type) {
19   for ( ; first < last; ++first)
20     destroy(&*first);
21 }
22 
23 // 如果元素的數值型別(value type)有 trivial destructor…
24 template <class ForwardIterator> 
25 inline void __destroy_aux(ForwardIterator, ForwardIterator, __true_type) {}

  可以看到,在__destroy函数里,利用了__type_traits<T>机制判断T类型是否具有无意义的析构函数,然后再根据判断结果进行参数推导,调用相应的函数,如果是无意义的析构函数就不做很任何事情,提升性能;如果是有意义的析构函数,就为该区间里的所有对象逐一调用析构函数。

  正如上所言,于一切自定义类型,__type_traits<T>机制默认认为其具有有意义的构造函数(除非是使用内部提供__type_traits机制的编译器就能自动识别该类型是否有意义,但大部分编译器缺乏这种特异功能),这样的结果过于保守,那么如何让__type_traits<T>机制能够为我们的自定义类型提取到真正的特性呢?答案就是自行为自己设计的类型提供__type_traits特化版本,明白地告诉编译器事实。举例,假设我自行定义了一个shape class,是一个没有默认构造函数的类型(即存在trivial defalt ctor),那么它的__type_traits特化版本应该是:

1 template<> struct __type_traits <Shade> {
2     typedef __true_type    has_trivial_default_constructor;
3     typedef __false_type    has_trivial_copy_constructor;
4     typedef __false_type    has_trivial_assignment_operator;
5     typedef __false_type    has_trivial_destructor;
6     typedef __false_type    is_POD_type;
7 }

  究竟一个类什么时候该有自己的non-trivial defalt ctor,non-trivial copy ctor,non-trivial assignment ctor,non-trivial dtor?一个简单的判断准则就是:如果类内包含指针成员,并且对它进行内存动态配置,那么这个类就需要实现出自己的non-trivial-xxx。

posted @ 2019-10-21 20:04  羽园原华  阅读(397)  评论(0编辑  收藏  举报