更多精彩内容,请关注微信公众号:后端技术小屋编程
traits
(译做萃取)是C++中一种特殊的编程技法,它是模板元编程最直接的用例之一。经过traits
,能够抽取模板入参类型的各类属性。接下来咱们经过STL中最多见的几种traits
举例说明。后端
__type_traits
用于判断类型是否为trival(译做平凡)。数组
若是一个类型是trivial
的,则能够静态初始化,能够用memcpy直接复制数据而不是必须用copy构造函数。其生存期始于它的对象存储被定义,无需等到构造函数完成。在执行ctor, copy, move, assign, ctor时,能够采用最有效率的方法:即不执行编译器自动生成的ctor, copy, assign, ctor, 取而代之的是malloc, free, memcpy这类操做。微信
举个栗子:_Destroy
用于销毁容器迭代器区间[__first, __last)
内全部对象。
而其调用链以下:函数
_Destroy -> __destroy -> __destroy_aux
在实现上,源码分析
_Destory
首先使用__VALUE_TYPE
获取指向容器中对象的指针类型,并将其实例做为入参传入到__destory
中。__destory
中,使用__type_traits<_Tp>::has_trivial_destructor
判断容器中对象类型是否为trivial
,并调用__destroy_aux
。__destroy_aux
函数根据_Tp
是否为trivial
实现了两个不一样的版本。在trivial
版本中,什么都不作,由于trivial
类型没有显式定义析构函数。在非trivial
函数中,则必须调用_Tp
的析构函数。咱们注意到,这里使用__type_traits
用于识别_Tp
类型是否trivial
布局
template <class _ForwardIterator> void __destroy_aux(_ForwardIterator __first, _ForwardIterator __last, __false_type) { for ( ; __first != __last; ++__first) destroy(&*__first); } template <class _ForwardIterator> inline void __destroy_aux(_ForwardIterator, _ForwardIterator, __true_type) {} template <class _ForwardIterator, class _Tp> inline void __destroy(_ForwardIterator __first, _ForwardIterator __last, _Tp*) { typedef typename __type_traits<_Tp>::has_trivial_destructor _Trivial_destructor; __destroy_aux(__first, __last, _Trivial_destructor()); } template <class _ForwardIterator> inline void _Destroy(_ForwardIterator __first, _ForwardIterator __last) { __destroy(__first, __last, __VALUE_TYPE(__first)); }
也许你的问题来了,什么样的类型才可称之为trivial的?this
若是一个类型知足如下条件中的至少一个,则称其为非trival;不然称其为trival 1. 显式定义了构造函数(ctor), 复制构造函数(copy), 移动构造函数(move),赋值运算符(assign), 或析构函数(ctor)之中任何一个。 2. 类中有非POD类型成员 3. 有虚函数 4. 有虚基类
到这里也许你的问题会更多了,到底什么是POD
类型?指针
POD = Plain Old Data 根据维基百科的定义,POD类型包括标量类型和POD类类型。POD在源代码兼容于ANSI C时很是重要。POD对象与C语言的对应对象具备共同的一些特性,包括初始化,复制,内存布局,寻址等。 标量类型包括: 1. 算数类型(整数/浮点/字符/布尔) 2. 枚举类型 3. 指针类型(空指针/对象指针/函数指针) 4. 指针到成员类型(例如T C::* 指向类C的类型为T的数据成员的指针) POD类类型是指聚合类(经过struct/union聚合)或数组,也不具备下述成员: 1. 指针到成员类型的非静态数据成员 2. 非POD类型的非静态数据成员 3. 引用类型的非静态数据成员 4. 显式定义的拷贝和赋值算子 5. 显式定义的析构函数 6. 若是是聚合类且含有显式定义的构造函数,私有/保护的非静态成员函数,基类,虚函数 综上,不符合以上6条的聚合类/数组才可称之为POD类类型。
在实现上,首先定义通用模板类__type_traits
。这里全部的计算都是基于类型的,所以使用__true_type
和__false_type
分别表示逻辑真/假类型。code
从代码能够看到,缺省状况下,_Tp
中默认构造函数/复制构造函数/赋值操做符/析构函数都不是trivial
的,_Tp
也不是POD类型。
template <class _Tp> 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; }; struct __true_type { }; struct __false_type { };
其次,对于全部标量类型,定义特化模板类__type_traits
,由于标量类型,没有定义默认构造/复制构造/复制操做符/析构函数,标量类型也属于POD类型。标量类型包含:
bool char signed char unsigned char wchar_t short unsigned short int unsigned int long unsigned long unsigned long long float double long double Tp* char* signed char* unsigned char*, const char* const signed char* const unsigned char*
_Is_integer用于判断类型是否为整数类型
举个例子:若是vector中元素类型_Tp
是整数类型。在这种状况下,若是不分区整数类型,那么编译器便没法区分vector<int> a(10, 1);
该使用如下代码中第一种构造函数仍是第二种,由于_InputIterator
只是一个模板参数,它能够是真正的迭代器类型,也能够是其余任何一种类型。
为了避免使编译器犯难,咱们须要在编译期间决定_InputIterator
是否为整数类型。这里用到了_Is_integer
,它断定_InputIterator
类型,并返回__true_type
或__false_type
。对应的,_M_initialize_aux
针对_InputIterator
是否为整形也实现了两个版本。最终使得vector
构造在两种不一样状况下,保持了各自的语义:
// 构造函数1 vector(size_type __n, const _Tp& __value, const allocator_type& __a = allocator_type()) : _Base(__n, __a) { _M_finish = uninitialized_fill_n(_M_start, __n, __value); } // 构造函数2 // Check whether it's an integral type. If so, it's not an iterator. template <class _InputIterator> vector(_InputIterator __first, _InputIterator __last, const allocator_type& __a = allocator_type()) : _Base(__a) { typedef typename _Is_integer<_InputIterator>::_Integral _Integral; _M_initialize_aux(__first, __last, _Integral()); }
首先,定义通用模板函数_Is_integer
。缺省状况下全部的类型都不是整型。
template <class _Tp> struct _Is_integer { typedef __false_type _Integral; };
其次,定义特化模板函数_Is_integer
,对于如下类型,_Integral
为__true_type
。
bool char signed char unsigned char wchar_t short unsigned short int unsigned int long unsigned long long long unsigned long long
PS: STL中远远不止上述两种traits,例如还有__char_traits
(见STL源码分析--string)、iterator_traits
(见STL源码分析--iterator)、_Alloc_traits
(见STL源码分析--内存分配器),在此留给读者自行分析
更多精彩内容,请关注微信公众号: 悟空者说
推荐阅读
更多精彩内容,请扫码关注微信公众号:后端技术小屋。若是以为文章对你有帮助的话,请多多分享、转发、在看。