__type_traits:双底线是说明这是SGI STL内部使用的东西,不在STL标准范围之内。iterator_traits负责萃取迭代器(iterator)的特性。而__type_traits则负责萃取型别(type)的特性。

我们所关注的型别特性是指:这个型别是否具备non-trivial defalt ctor,non-trivial copy cotr ,non-trivial assignment operator,non-trivial dtor[1] ,如果没有这些non-trivial(有意义)函数,那么我们在对这个型别进行构造、析构、拷贝、赋值操作时,就可以采用最效的措施(根本不调用那些身居高位的constructor,destructor等,而采用内存直接处理操作如malloc(),memcpy()等,获得最高效率)。

一、那么什么是trivial or non-trivial呢?

这个trivial和non-trivial是对类的四种函数来说的:

  • 构造函数(ctor)
  • 拷贝构造函数(copy)
  • 赋值函数(assignment)
  • 析构函数(dtor)

如果至少满足下面3条里的一条:

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

那么上面的四种函数是non-trivial函数,比如non-trivial ctor、non-trivial copy…POD意思是Plain Old Data(普通旧数据),也就是C++的内建类型或传统的C结构体类型,POD类型必然有(trivial:无意义)trivial ctor/dtor/copy/assignment四种函数。

二、__type_traits提供了一种机制:就是在编译时观察这个型别有没有trivial函数,如果有,那么就能执行高效率的操作了,哇好开心

当我们准备对一个“元素型别未知”的数组执行copy操作时,如果我们能事先知道其元素性别是否有一个trivail copy contructor,那么就能够帮助我们是否可以使用快速的memcpy()或memmove()。即在编译时期针对不同型别完成函数派送。

我们希望程序之中可以这样运用__type_traits<T>:

 __type_traits<T>::has_trivial_default_constructor;
__type_traits<T>::has_trivial_copy_constructor;
__type_traits<T>::has_trivial_assignment_operator;
__type_traits<T>::has_trivial_destructor;
__type_traits<T>::is_POD_type;

我们希望利用响应结果来进行参数推导,而编译器只有面对class object形式的参数才会做参数推导,所以上述的返回结果应该如下:这两个classes没有任何成员,不会带来额外负担,却又能够标识真假。

struct __true_type {};

struct __false_type {};

为了达成上述五个式子,__type_traits内必须定义一些typedefs,其值不是__true_type就是__false_type:

  template <class type>
struct __type_traits {
typedef __true_type this_dummy_member_must_be_first; //为了确保万一编译器也使用一个名为__type_traits而其实与此处定义并无任何关联的template时,事情顺利工作。 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;
};

针对__false_type,由于编译器有可能为各型别自动产生专属的__type_traits特化版本,所以你可以将上述成员次序重新排列,也可以移除上述任何成员,绝对不可以在没有改变编译器对应名称的情况下对成员重新命名,只有在编译器中加上适当支持时,否则新加入成员会被视为一般成员。

SGI将所有的内嵌型别定义成__false_type,因为SGI先定义最保守的值,然后再针对每一个标量型别(scalar types)设计适当的__type_traits特化版本:

# 什么是标量型别:标量类型(Scalar type)是相对复合类型(Compound type)来说的:标量类型只能有一个值,而复合类型可以包含多个。

上述__type_traits可以接受任何型别的参数,五个typedefs将由以下管道获得实值

  • 一般具现体(general instantiation),内含对所有型别都有效的保守值,上述各个has_trivial_xxx型别都被定义为_false_type,就是对所有型别都必定有效的保守值。
# 实例化( Instantiation) 与特化( Specialization),在 template 中,「以实际值( actual values)做为 template arguments,从而产生常规的( regular)class、 function 或 member function」,这个过程称为 「 template 实例化」( template instantiation)。
这 些 随 之 产 生 的 物 体(entity ;包 括 class, function 或 member function ) 通 称为 特 化 体 ( specialization)。( 译注:我所阅读的众多泛型编程书籍中,很少将这些物体称为特化体,较 常称呼的是具现体, instantiation)
  • 经过声明的特化版本,例如<type_traits.h>内对所有C++标量型别(scalar types)提供了对应的特化版本声明(每个型别的值都是__true_type)。
  • 某些编译器(如Silicon Graphics N32和N64编译器)会自动为所有型别提供适当的特化版本(这个是真的牛逼,不过到底有没有候捷对它表示疑惑)

三、对所有C++标量型别所定义的__type_traits的特化版本,非常必要

针对C++基本型别char,signed char ,unsigend char ,short,unsigned short ,int ,unsigned int,long,unsigned long,float,double,long double提供特化版本,每个型别的值都是__true_type,表示这些型别都可以采用最快速方式来进行拷贝或赋值操作。

__STL_TEMPLATE_NULL定义为template<>即class template explicit specialization(类模板显式特化)

 //针对char的特化版本
__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;
};
//针对signed char的特化版本
__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;
};
//针对unsigned char的特化版本
__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;
};
//针对short的特化版本
__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;
};
...未全部给出

注意:还有针对原生指针涉设计的__type_traits偏特化版本:原生指针也被视为一种标量型别。

 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;
};

四、__type_traits的应用

1.uninitialized_fill_n()全局函数

 template <class ForwardIterator, class Size, class T>
inline ForwardIteratoruninitialized_fill_n(ForwardIterator first, Size n,const T& x) {
return __uninitialized_fill_n(first, n, x, value_type(first));
}

该函数以x为蓝本,自迭代器first开始构造n个元素。为求取最大效率,首先以value_type()萃取出迭代器first的 value type,再利用__type_traits判断型别是否为POD类型

 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;
return __uninitialized_fill_n_aux(first, n, x, is_POD());
}

对于“是否是POD型别”采取最适当的措施:

  //如果是POD型别,就会派送到这里。如果copy constructor等同于assignment,而且有trivial destructor。以下就有效:
template <class ForwardIterator, class Size, class T>
inline ForwardIterator
__uninitialized_fill_n_aux(ForwardIteratorfirst, Size n,
const T& x,__true_type) {
return fill_n(first, n, x); //交由高阶函数执行,如下所示
}
//以下是定义于<stl_algobase.h>中的fill_n
template <class OutputIterator, class Size, class T>
OutputIteratorfill_n(OutputIterator first, Size n, const T& value) {
for ( ; n > ; --n, ++first)
*first = value;
return first;
} //如果不是POD型别:就需要老老实实的一个一个在未初始化的内存中调用构造函数了!好伤心!
template <class ForwardIterator, class Size, class T>
ForwardIterator
__uninitialized_fill_n_aux(ForwardIteratorfirst, Size n,
const T& x,__false_type) {
ForwardIterator cur = first;
for ( ; n > ; --n, ++cur)
construct(&*cur, x);
return cur;
}

2.destroy()函数

3.copy()全局函数

这个函数有非常多的特化(speialization)与强化(refinement)版本,殚精竭虑都是为了效率考虑,希望在最适当的情况下次采用最“雷霆万钧”的手段:

 //拷贝一个数组,其元素性别为任意性别,根据情况采用最有效率的拷贝手段
template <class T>inline void copy(T* source,T* destination,int n){
copy(source,destination,n,typename __type_traits<T>::has_trivial_copy_constructor());
} //拷贝一个数组,其元素性别拥有non-trivial copy constructors(有意义拷贝构造函数)
template <class T>void copy(T* source,T* destination,int __false_type)
{...} //拷贝一个数组,其元素型别拥有trivial copy constructors
//可借助memcpy()完成工作
template <class T>void copy(T* source,T* destination,int ,__true_type)
{...}

五、自己定义类型的__type_traits

假设自行定义了一个Shape class,除了某些厉害的编译器(如Silicon Graphics N32和N64编译器),对于一般编译器,__type_traits针对Shape萃取出来的每个特性都是__false_type,即使Shape是个POD类型。这样虽然过于保守,但是别无选择,除非针对Shape,自行设计一个__type_traits版本:

 template<> struct __type_traits<Shape> {
typedef __false_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 __false_type is_POD_type;
};

究竟什么时候一个class该有自己non-trivial_xxx,即如果class内含指针成员,并且对它进行内存动态配置,那么这个class就需要实现自己的non-trivial-xxx

六、总结

有了__type_traits,即使你不能针对自己定义的型别,设计__type_traits特化版本,但是对于C++标量型别,我们可以采用最有效的拷贝或赋值操作,因为每个标量型别都有对应的__type_traits版本,其中每个typedef的值都是__true_type。

 七、参考文献

[1].https://freezhongzi.info/index.php/2012/11/09/c-trivial%e5%92%8cnon-trivial%e6%9e%84%e9%80%a0%e5%87%bd%e6%95%b0%e5%8f%8apod%e7%b1%bb%e5%9e%8b/

STL之__ type_traits的更多相关文章

  1. C++ new(3)

    转载自:http://www.builder.com.cn/2008/0104/696370.shtml “new”是C++的一个关键字,同时也是操作符.关于new的话题非常多,因为它确实比较复杂,也 ...

  2. std::set 中内部元素有序条件删除的理解

    std::set 中内部元素有序条件删除的理解 1. std::set中的元素是有序排列的 注意:Set集合中的元素通过iterator的引用,但是不能修改. 元素排序: (1)元素中实现比较oper ...

  3. 深入C++的new

    new”是C++的一个关键字,同时也是操作符.关于new的话题非常多,因为它确实比较复杂,也非常神秘,下面我将把我了解到的与new有关的内容做一个总结. new的过程 当我们使用关键字new在堆上动态 ...

  4. ZT C++关键字new学习

    http://blog.csdn.net/waken_ma/article/details/4007914 C++关键字new学习 很多新手对C++关键字new可能不是很了解吧,今天我一起来学习一下. ...

  5. C++ new 的用法

    原文链接:http://www.builder.com.cn/2008/0104/696370.shtml “new”是C++的一个关键字,同时也是操作符.关于new的话题非常多,因为它确实比较复杂, ...

  6. C++ STL 学习笔记__(7)Set和multiset容器

    10.2.8 Set和multiset容器 set/multiset的简介 ²  set是一个集合容器,其中所包含的元素是唯一的,集合中的元素按一定的顺序排列.元素插入过程是按排序规则插入,所以不能指 ...

  7. C++ STL 学习笔记__(6)优先级队列priority_queue基本操作

    10.2.7优先级队列priority_queue v  最大值优先级队列.最小值优先级队列 v  优先级队列适配器 STL priority_queue v  用来开发一些特殊的应用,请对stl的类 ...

  8. C++ STL 学习笔记__(8)map和multimap容器

    10.2.9 Map和multimap容器 map/multimap的简介 ²  map是标准的关联式容器,一个map是一个键值对序列,即(key,value)对.它提供基于key的快速检索能力. ² ...

  9. C++ STL 学习笔记__(5)list

    10.2.6List容器 List简介 ²  list是一个双向链表容器,可高效地进行插入删除元素. ²  list不可以随机存取元素,所以不支持at.(pos)函数与[]操作符.It++(ok) i ...

随机推荐

  1. CSS 案例学习

    1.样式 display:inline-block;可改变a标签,合其可定义宽高 2.a:hover表示鼠标经过 3.background:url(110.png) bottom 表示:给链接一个图片 ...

  2. hdu6393 /// 树链剖分

    题目大意: 给定n q 在n个点n条边的图中 进行q次操作 0 k x 为修改第k条边的值为x 1 x y 为查询x到y的最短路 https://blog.csdn.net/nka_kun/artic ...

  3. hdu6319 Ascending Rating /// 单调队列

    题目大意: 给定n m k,p q r mod, 给出序列的前k项 之后的按公式 a[i]=(p×a[i-1]+q×i+r)%mod 递推 求序列中每个长度为m的连续区间的 该区间最大值与第一位的位置 ...

  4. 在ubuntu16下安装virtualenv+virtualenvwrapper

    ubuntu16已经安装好了py2和py3(自带的) bigni@bigni-Latitude-E6230:~/python_file/Django_project$ python python py ...

  5. python生成阿里云云直播推流播流地址

    申请一个阿里云账号,进入控制台,添加云直播工能,就可以获得相关数据, 直接上代码,阿里云接口文档https://cloud.tencent.com/document/product/267/7977 ...

  6. 使用 Google Chrome 数据抓包方式免费下载收费音乐

    对于每个有音乐细胞的 boys & girls 来说,听音乐的时候是不是会经常遇到如下图的问题. 自从音乐进入正版之后,很多歌曲只有付费用户才可以下载.虽然可以在线听,可是以我的倔脾气,就是喜 ...

  7. js循环给li绑定事件实现和弹出对应的索引

    原文:http://www.cnblogs.com/wuchuanlong/p/5945286.html 方法一,动态添加click事件,并添加属性 var itemli = document.get ...

  8. C/C++ warning C4251: class ... 需要有 dll 接口由 class“..” 的客户端使用

    { 在DLL编程中, 如果调用模版类, 则可能出现类似以下的错误: 1>xclock.h(29): warning C4251: “XClock::m_FileName”: class“std: ...

  9. JavaScript中的节流和防抖

    节流: 在规定时间内,多次触发事件,但是只执行一次 场景:输入框搜索,地图渲染 优化用户体验 /** * 节流 规定时间内不管触发多少次只执行一次 * @param {Function} fn 实际要 ...

  10. delphi 特殊窗体

    delphi 窗体阴影 放窗体创建事件里面 SetClassLong(Handle, GCL_STYLE, GetClassLong(Handle, GCL_STYLE) or CS_DROPSHAD ...