STL源码分析--traits

简介: STL源码分析--traits



  • 1 __type_traits:trivial判定


  • 1.1 为什么要判定trivial类型


  • 1.2 trivial类型的定义


  • 1.3 trival类型判定的实现


  • 2 _Is_integer: 整型判定


  • 2.1 为什么要进行整型判定


  • 2.2 整型判定的实现



traits(译作萃取)是C++中一种特殊的编程技法,它是模板元编程最直接的用例之一。通过traits,可以抽取模板入参类型的各种属性。接下来我们通过STL中最常见的几种traits举例说明。



1 __type_traits:trivial判定


__type_traits用于判断类型是否为trival(译作平凡)。



1.1 为什么要判定trivial类型


如果一个类型是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));
}




1.2 trivial类型的定义


也许你的问题来了,什么样的类型才可称之为trivial的?


如果一个类型满足以下条件中的至少一个,则称其为非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类类型。




1.3 trival类型判定的实现


在实现上,首先定义通用模板类__type_traits。这里所有的计算都是基于类型的,因此使用__true_type__false_type分别表示逻辑真/假类型。


从代码可以看到,缺省情况下,_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*




2 _Is_integer: 整型判定


_Is_integer用于判断类型是否为整数类型




2.1 为什么要进行整型判定


举个例子:如果vector中元素类型_Tp是整数类型。在这种情况下,如果不分区整数类型,那么编译器便无法区分vector<int> a(10, 1);该使用以下代码中第一种构造函数还是第二种,因为_InputIterator只是一个模板参数,它可以是真正的迭代器类型,也可以是其他任何一种类型。


为了不使编译器犯难,我们需要在编译期间决定_InputIterator是否为整数类型。这里用到了_Is_integer,它判定_InputIterator类型,并返回__true_type__false_type。对应的,_M_initialize_aux针对_InputIterator是否为整形也实现了两个版本。最终使得vector构造在两种不同情况下,保持了各自的语义:


  • 语义一:指定元素数量和初始值,构造vector


  • 语义二:指定起始iterator和终止iterator, 构造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());
  }




2.2 整型判定的实现


首先,定义通用模板函数_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



相关文章
|
20天前
|
存储 算法 C++
【C++】STL的基本用法
【C++】STL的基本用法
40 0
|
8月前
|
存储 编译器 C++
vector使用及简单实现【STL】【附题】
vector使用及简单实现【STL】【附题】
23 0
|
14天前
|
存储 算法 编译器
【STL】vector的底层原理及其实现
【STL】vector的底层原理及其实现
|
20天前
|
存储 算法 Linux
【STL】:vector用法详解
【STL】:vector用法详解
54 1
|
6月前
|
算法 Linux C++
【C++】STL之vector类概述-2
【C++】STL之vector类概述
30 0
|
6月前
|
存储 算法 C语言
【C++】STL之vector类概述-1
【C++】STL之vector类概述
36 0
|
10月前
|
存储 算法 编译器
【C++STL】“vector“用法 入门必备 超详细
【C++STL】“vector“用法 入门必备 超详细
|
存储 算法 C++
【C++】STL —— vector基本使用
【C++】STL —— vector基本使用
126 0
【C++】STL —— vector基本使用
c++STL vector的用法详解
c++STL vector的用法详解
94 0
|
C++ 容器
C++ STL__queue 的使用方法
C++ STL__queue 的使用方法
74 0

热门文章

最新文章