上节给出了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>:

 __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

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

 struct __true_type {};
struct __false_type {};

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

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

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

  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支持能力的编译器并无伤害,对于无该支持能力的编译器而言,实属必要。

 /* 以下针对 C++ 基本型別 char, signed char, unsigned char, short, unsigned short,
int, unsigned int, long, unsigned long, float, double, long double 提供特化版本。
注意,每一个成员的值都是 __true_type,表示这些型別都可采用最快速方式(例如 memcpy)
來进行拷贝动作或赋值动作。*/ //注意,SGI STL<stl_config.h>将以下出现的 _STL_TEMPLATE_NULL
//定义为template<>,是所谓的class template explicit specialization __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;
}; #else /* __STL_CLASS_PARTIAL_SPECIALIZATION */ 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;
}; 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;
}; 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;
};

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

 // 以下是 destroy() 第二版本,接受兩個迭代器。此函式是設法找出元素的數值型別,
// 進而利用 __type_traits<> 求取最適當措施。
template <class ForwardIterator>
inline void destroy(ForwardIterator first, ForwardIterator last) {
__destroy(first, last, value_type(first));
} // 判斷元素的數值型別(value type)是否有 trivial destructor
template <class ForwardIterator, class T>
inline void __destroy(ForwardIterator first, ForwardIterator last, T*) {
typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor;
__destroy_aux(first, last, trivial_destructor());
} // 如果元素的數值型別(value type)有 non-trivial destructor…
template <class ForwardIterator>
inline void
__destroy_aux(ForwardIterator first, ForwardIterator last, __false_type) {
for ( ; first < last; ++first)
destroy(&*first);
} // 如果元素的數值型別(value type)有 trivial destructor…
template <class ForwardIterator>
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特化版本应该是:

 template<> struct __type_traits <Shade> {
typedef __true_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;
}

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

STL源码剖析——Iterators与Traits编程#5 __type_traits的更多相关文章

  1. STL源码剖析——iterators与trait编程#2 Traits编程技法

    在算法中运用迭代器时,很可能用到其相应类型.什么是相应类型?迭代器所指对象的类型便是其中一个.我曾有一个错误的理解,那就是认为相应类型就是迭代器所指对象的类型,其实不然,相应类型是一个大的类别,迭代器 ...

  2. STL源码剖析——iterators与trait编程#4 iterator源码

    在前两节介绍了迭代器的五个相应类型,并讲述如何利用traits机制提取迭代器的类型,但始终是把iteartor_traits类分割开来讨论,这影响我们的理解,本节将给出iteator的部分源码,里面涵 ...

  3. STL源码剖析——iterators与trait编程#3 iterator_category

    最后一个迭代器的相应类型就是iterator_category,就是迭代器本身的类型,根据移动特性与实行的操作,迭代器被分为了五类: Input Iterator:这种迭代器所指的对象,不允许外界改变 ...

  4. STL源码剖析——iterators与trait编程#1 尝试设计一个迭代器

    STL的中心思想在于:将数据容器与算法分开,独立设计,再用一帖粘着剂将它们撮合在一起.而扮演粘着剂这个角色的就是迭代器.容器和算法泛型化,从技术角度来看并不困难,C++的模板类和模板函数可分别达成目标 ...

  5. 《STL源码剖析》----2.23 value_type()和__type_traits<>如何实现

    在2.13小节destory()第二版本接受两个迭代器找出元素类型,代码如下 其中value_type()判断出类型,__type_traits判断是否存在trivial destructor 在3. ...

  6. 《STL源码剖析》学习之traits编程

    侯捷老师在<STL源码剖析>中说到:了解traits编程技术,就像获得“芝麻开门”的口诀一样,从此得以一窥STL源码的奥秘.如此一说,其重要性就不言而喻了.      之前已经介绍过迭代器 ...

  7. 【STL 源码剖析】浅谈 STL 迭代器与 traits 编程技法

    大家好,我是小贺. 点赞再看,养成习惯 文章每周持续更新,可以微信搜索「herongwei」第一时间阅读和催更,本文 GitHub : https://github.com/rongweihe/Mor ...

  8. STL源码剖析 迭代器(iterator)概念与编程技法(三)

    1 STL迭代器原理 1.1  迭代器(iterator)是一中检查容器内元素并遍历元素的数据类型,STL设计的精髓在于,把容器(Containers)和算法(Algorithms)分开,而迭代器(i ...

  9. STL源码剖析之序列式容器

    最近由于找工作需要,准备深入学习一下STL源码,我看的是侯捷所著的<STL源码剖析>.之所以看这本书主要是由于我过去曾经接触过一些台湾人,我一直觉得台湾人非常不错(这里不涉及任何政治,仅限 ...

随机推荐

  1. 定时任务 Scheduled quartz

    在项目应用中往往会用到任务定时器的功能,比如某某时间,或者多少多少秒然后执行某个骚操作等.spring 支持多种定时任务的实现,其中不乏自身提供的定时器.接下来介绍一下使用 spring 的定时器和使 ...

  2. go 学习 (五):包管理

    一.设置环境变量 二.启用 go modules 功能 并设置代理 https://goproxy.io/zh/ 补充: GO111MODULE  有三个值:on.off.auto GO111MODU ...

  3. Python微信操控(itchat)

    itchat是一个开源的微信个人号接口,使用python调用微信从未如此简单. 开源地址 https://github.com/littlecodersh/ItChat 文档: https://itc ...

  4. eclipse 配置python环境 json 插件

    windows->install new software add 配置python 环境: name:pydev(可随意写) url:http://pydev.org/updates/ (如果 ...

  5. HBASE-LSM树(转载)

    HBASE-LSM树 1.B+树 关于B树.B+树.B树的了解参考:* http://blog.csdn.net/v_july_v/article/details/6530142 优点: 走进搜索引擎 ...

  6. 纯js制作九宫格

    Demo实现了对任意方格进行拖拽,可以交换位置,其中Demo-1利用了勾股定理判断距离! Demo-1整体思路: 1.首先div实现自由移动,一定需要脱离标准文档流,所以我们给它使用绝对定位. 2.利 ...

  7. OPPO-Java面试-社招-一面(2019/07)

    个人情况 2017年毕业,普通本科,计算机科学与技术专业,毕业后在一个二三线小城市从事Java开发,2年Java开发经验.做过分布式开发,没有高并发的处理经验,平时做To G的项目居多.写下面经是希望 ...

  8. Linux+Apache环境下安装SSL证书

    一.安装证书 (温馨提示:安装证书前请先备份您需要修改的服务器配置文件) 1.确认证书文件及证书路径.  例证书文件为:zzidc.com.jks,放置目录为Tomcat的conf目录下.  2.配置 ...

  9. 牛顿插值法(c++)

    X Y 0.40 0.41075 0.55 0.57815 0.65 0.69675 0.80 0.88811 0.90 1.02652 1.05 1.25382 #include using nam ...

  10. 2015-2016-2《Java程序设计》团队博客5

    一.项目进展 本周将所有的项目代码全部进行了汇总总结,归纳在了一起,进行整体的测试.虽然在编写的时候很顺利,也就是片段代码问题不大,但是汇总到一起时还是产生了冲突与不对等的问题,所以我们只能仔细地从细 ...