前言
上一篇文章讲了 iterator_traits 的编程技法,非常棒适度弥补了 C++ 语言的不足。 STL 只对迭代器加以规范,制定了 iterator_traits 这样的东西。 CGI 把这种技法进一步扩大到迭代器以外的世界,于是有了 __type_traits。双划线前缀词意指只是 CGI STL 内部所有的东西,不在 STL 标准规范之内。
__type_traits 定义和作用
在源码中 __type_traits 在 type_traits.h 文件定义,以下是泛化版本:
struct __true_type { }; struct __false_type { }; //泛化版本 template <class type> struct __type_traits { typedef __true_type this_dummy_member_must_be_first; typedef __false_type has_trivial_default_constructor; typedef __false_type has_trivial_copy_constructor; typedef __false_type has_trivial_assignment_operator; typedef __false_type has_trivial_destructor; typedef __false_type is_POD_type; };
iterator_traits 负责萃取迭代器的特性,__type_traits 则负责萃取型别特性。这个特性主要是指:has_trivial_default_constructor,has_trivial_copy_constructor,has_trivial_assignment_operator,has_trivial_destructor,is_POD_type 这五种,表示是否有不重要的构造、析构、拷贝和赋值等操作,是真假属性。如果答案是肯定的,我们对这个型别进行相应操作时就可以采用更有效的措施,不调用 constructor 和 destructor 等,而采用内存直接处理操作如 malloc()、memcpy() 等等,获得最高效率。例如 int, float. double, char 等普通类型这些属性为真,不需要调用类型自身的构造和析构等。而 string 类或内嵌型别的类这些属性则是默认为假,需要调用自身的构造和析构等。这里的解释和侯捷《STL源码剖析》讲的不一样,侯捷引入 non-trivial defalt ctor 这种学术性的名称,可能这样更加专业,但我觉得不能直观表现源码,但意思是一样的,如果有问题欢迎留言讨论。
__true_type 和 __false_type 没有任何成员,不会带来额外负担,却又能标示真假。用 bool 也能达到目的,但是 bool 变量不能达到编译期绑定。CGI 把所有内嵌型别都定义为 __false_type,即定义出最保守的值。源码也针对普通类型设计特化版本,具体如下:
__STL_TEMPLATE_NULL struct __type_traits<char> { typedef __true_type has_trivial_default_constructor; typedef __true_type has_trivial_copy_constructor; typedef __true_type has_trivial_assignment_operator; typedef __true_type has_trivial_destructor; typedef __true_type is_POD_type; }; __STL_TEMPLATE_NULL struct __type_traits<signed char> { typedef __true_type has_trivial_default_constructor; typedef __true_type has_trivial_copy_constructor; typedef __true_type has_trivial_assignment_operator; typedef __true_type has_trivial_destructor; typedef __true_type is_POD_type; }; __STL_TEMPLATE_NULL struct __type_traits<unsigned char> { typedef __true_type has_trivial_default_constructor; typedef __true_type has_trivial_copy_constructor; typedef __true_type has_trivial_assignment_operator; typedef __true_type has_trivial_destructor; typedef __true_type is_POD_type; }; __STL_TEMPLATE_NULL struct __type_traits<short> { typedef __true_type has_trivial_default_constructor; typedef __true_type has_trivial_copy_constructor; typedef __true_type has_trivial_assignment_operator; typedef __true_type has_trivial_destructor; typedef __true_type is_POD_type; }; __STL_TEMPLATE_NULL struct __type_traits<unsigned short> { typedef __true_type has_trivial_default_constructor; typedef __true_type has_trivial_copy_constructor; typedef __true_type has_trivial_assignment_operator; typedef __true_type has_trivial_destructor; typedef __true_type is_POD_type; }; __STL_TEMPLATE_NULL struct __type_traits<int> { typedef __true_type has_trivial_default_constructor; typedef __true_type has_trivial_copy_constructor; typedef __true_type has_trivial_assignment_operator; typedef __true_type has_trivial_destructor; typedef __true_type is_POD_type; }; __STL_TEMPLATE_NULL struct __type_traits<unsigned int> { typedef __true_type has_trivial_default_constructor; typedef __true_type has_trivial_copy_constructor; typedef __true_type has_trivial_assignment_operator; typedef __true_type has_trivial_destructor; typedef __true_type is_POD_type; }; __STL_TEMPLATE_NULL struct __type_traits<long> { typedef __true_type has_trivial_default_constructor; typedef __true_type has_trivial_copy_constructor; typedef __true_type has_trivial_assignment_operator; typedef __true_type has_trivial_destructor; typedef __true_type is_POD_type; }; __STL_TEMPLATE_NULL struct __type_traits<unsigned long> { typedef __true_type has_trivial_default_constructor; typedef __true_type has_trivial_copy_constructor; typedef __true_type has_trivial_assignment_operator; typedef __true_type has_trivial_destructor; typedef __true_type is_POD_type; }; __STL_TEMPLATE_NULL struct __type_traits<float> { typedef __true_type has_trivial_default_constructor; typedef __true_type has_trivial_copy_constructor; typedef __true_type has_trivial_assignment_operator; typedef __true_type has_trivial_destructor; typedef __true_type is_POD_type; }; __STL_TEMPLATE_NULL struct __type_traits<double> { typedef __true_type has_trivial_default_constructor; typedef __true_type has_trivial_copy_constructor; typedef __true_type has_trivial_assignment_operator; typedef __true_type has_trivial_destructor; typedef __true_type is_POD_type; }; __STL_TEMPLATE_NULL struct __type_traits<long double> { typedef __true_type has_trivial_default_constructor; typedef __true_type has_trivial_copy_constructor; typedef __true_type has_trivial_assignment_operator; typedef __true_type has_trivial_destructor; typedef __true_type is_POD_type; }; #ifdef __STL_CLASS_PARTIAL_SPECIALIZATION template <class T> struct __type_traits<T*> { typedef __true_type has_trivial_default_constructor; typedef __true_type has_trivial_copy_constructor; typedef __true_type has_trivial_assignment_operator; typedef __true_type has_trivial_destructor; typedef __true_type is_POD_type; }; #endif
__type_traits 应用
__type_traits 在源码中的应用很多,我这里只举 uninitialized_fill_n 这个全局函数来说明,源码定义在 stl_uninitialized.h 文件,具体如下:
template<class ForwardIterator, class Size, class T> inline ForwardIterator __uninitialized_fill_n_aux(ForwardIterator first, Size n, const T& x, __true_type) { return fill_n(first, n, x); //fill_n定义下面给出 } template<class ForwardIterator, class Size, class T> ForwardIterator __uninitialized_fill_n_aux(ForwardIterator first, Size n, const T& x, __false_type) { ForwardIterator cur = first; __STL_TRY { for ( ; n > 0; --n, ++cur) construct(&*cur, x); //上面分析过,__false_type需调用自身构造 return cur; } __STL_UNWIND(destroy(first, cur)); } template<class ForwardIterator, class Size, class T, class T1> inline ForwardIterator __uninitialized_fill_n(ForwardIterator first, Size n, const T& x, T1*) { typedef typename __type_traits<T1>::is_POD_type is_POD; //根据is_POD的属性绑定不一样的函数 return __uninitialized_fill_n_aux(first, n, x, is_POD()); } template<class ForwardIterator, class Size, class T> inline ForwardIterator uninitialized_fill_n(ForwardIterator first, Size n, const T& x) { return __uninitialized_fill_n(first, n, x, value_type(first)); }
上面调用的 fill_n 在 stl_glgobase.h 文件定义,调用的函数源码如下:
//填充元素到半开半闭区间[first, last) template <class ForwardIterator, class T> void fill(ForwardIterator first, ForwardIterator last, const T& value) { for ( ; first != last; ++first) *first = value; }
我在程序中 debug 调试验证过,当 T 类型为普通类型 int,函数绑定的是调用 fill_n的函数,而当 T 是自定义类型,函数绑定的是含有 construct 的函数。验证了上述分析过程的正确性。
至此,我们发现,traits 真是个好东西,比 if-else 好多了,同时是在编译期绑定的,效率更高了,好神奇的技术,STL 源码值得继续学习下去!