2019独角兽企业重金招聘Python工程师标准>>>

Traits不是一种语法特性,而是一种模板编程技巧。Traits在C++标准库,尤其是STL中,有着不可替代的作用。

如何在编译期间区分类型

下面我们看一个实例,有四个类,Farm、Worker、Teacher和Doctor,我们需要区分他们是脑力劳动者还是体力劳动者。以便于做出不同的行动。

这里的问题在于,我们需要为两种类型提供一个统一的接口,但是对于不同的类型,必须做出不同的实现

我们不希望写两个函数,然后让用户去区分。

于是我们借助了函数重载,在每个类的内部内置一个work_type,然后根据每个类的word_type,借助强大的函数重载机制,实现了编译期的类型区分,也就是编译期多态。

代码如下:

#include <iostream>
using namespace std;//两个标签类
struct brain_worker {}; //脑力劳动
struct physical_worker {}; //体力劳动class Worker
{
public:typedef physical_worker worker_type;
};class Farmer
{
public:typedef physical_worker worker_type;
};class Teacher
{
public:typedef brain_worker worker_type;
};class Doctor
{
public:typedef brain_worker worker_type;
};template <typename T>
void __distinction(const T &t, brain_worker)
{cout << "脑力劳动者" << endl;
}template <typename T>
void __distinction(const T &t, physical_worker)
{cout << "体力劳动者" << endl;
}template <typename T>
void distinction(const T &t)
{typename T::worker_type _type; //为了实现重载
    __distinction(t, _type);
}int main(int argc, char const *argv[])
{Worker w;distinction(w);Farmer f;distinction(f);Teacher t;distinction(t);Doctor d;distinction(d);return 0;
}

在distinction函数中,我们先从类型中提取出worker_type,然后根据它的类型,选取不同的实现。

问题来了,如果不在类中内置worker_type,或者有的类已经写好了,无法更改了,那么怎么办?

使用Traits

我们的解决方案是,借助一种叫做traits的技巧。

我们写一个模板类,但是不提供任何实现:

//类型traits
template <typename T>
class TypeTraits;

然后我们为每个类型提供一个模板特化:

//为每个类型提供一个特化版本
template <>
class TypeTraits<Worker>
{
public:typedef physical_worker worker_type;
};template <>
class TypeTraits<Farmer>
{
public:typedef physical_worker worker_type;
};template <>
class TypeTraits<Teacher>
{
public:typedef brain_worker worker_type;
};template <>
class TypeTraits<Doctor>
{
public:typedef brain_worker worker_type;
};

然后在distinction函数中,不再是直接寻找内置类型,而是通过traits抽取出来。

template <typename T>
void distinction(const T &t)
{//typename T::worker_type _type;typename TypeTraits<T>::worker_type _type;__distinction(t, _type);
}

上面两种方式的本质区别在于,第一种是在class的内部内置type,第二种则是在类的外部,使用模板特化,class本身对于type并不知情。

两种方式结合

上面我们实现了目的,类中没有work_type时,也可以正常运行,但是模板特化相对于内置类型,还是麻烦了一些。

于是,我们仍然使用内置类型,也仍然使用traits抽取work_type,方法就是为TypeTraits提供一个默认实现,默认去使用内置类型,把二者结合起来。

这样我们去使用TypeTraits<T>::worker_type时,有内置类型的就使用默认实现,无内置类型的就需要提供特化版本。

class Worker
{
public:typedef physical_worker worker_type;
};class Farmer
{
public:typedef physical_worker worker_type;
};class Teacher
{
public:typedef brain_worker worker_type;
};class Doctor
{
public:typedef brain_worker worker_type;
};//类型traits
template <typename T>
class TypeTraits
{
public:typedef typename T::worker_type worker_type;
};

OK,我们现在想添加一个新的class,于是我们有两种选择,

一是在class的内部内置work_type,通过traits的默认实现去抽取type。

一种是不内置work_type,而是通过模板的特化,提供work_type。

例如:

class Staff
{
};template <>
class TypeTraits<Staff>
{
public:typedef brain_worker worker_type;
};

测试仍然正常:

Staff s;
distinction(s);

进一步简化

这里我们考虑的是内置的情形。对于那些要内置type的类,如果type个数过多,程序编写就容易出现问题,我们考虑使用继承,先定义一个base类:

template <typename T>
struct type_base
{typedef T worker_type;
};

所有的类型,通过public继承这个类即可:

class Worker : public type_base<physical_worker>
{
};class Farmer : public type_base<physical_worker>
{
};class Teacher : public type_base<brain_worker>
{
};class Doctor : public type_base<brain_worker>
{
};

看到这里,我们应该明白,traits相对于简单内置类型的做法,强大之处在于:如果一个类型无法内置type,那么就可以借助函数特化,从而借助于traits。而内置类型仅仅使用于class类型。

以STL中的迭代器为例,很多情况下我们需要辨别迭代器的类型,

例如distance函数计算两个迭代器的距离,有的迭代器具有随机访问能力,如vector,有的则不能,如list,我们计算两个迭代器的距离,就需要先判断迭代器能否相减,因为只有具备随机访问能力的迭代器才具有这个能力。

我们可以使用内置类型来解决。

可是,许多迭代器是使用指针实现的,指针不是class,无法内置类型,于是,STL采用了traits来辨别迭代器的类型。

最后,我们应该认识到,traits的基石是模板特化

下篇文章,我们使用traits,来辨别一个类型是否是pod类型。

转载于:https://my.oschina.net/inevermore/blog/388694

模板:什么是Traits相关推荐

  1. 数据字典模板_C++ 模板元编程:一种屠龙之技

    概述 模板元编程可在编译时完成一些计算,说它是屠龙之技,是因为模板元编程 似乎很厉害的样子. 似乎没有地方可以用上. 假如只从实际工程出发,没有太大必要研究模板元编程.只是我还是想写写这个主题,感叹一 ...

  2. c++ traits技法理解

    终于理解了c++ traits技法,不得不说它对c++的作用可以说是很大,让编程效率可以提升很多,为什么这多说,就是当我们在写了一个模板后,traits技法可以让这个模板的通用性更强,模板偏特化就会在 ...

  3. 一个return引发的血案 - 自己动手实现allocator

    最近在追旧番<STL代码剖析>.真的是很旧很旧的番了,STL在94年开始走入STL,这本书则是2002年出版的,C++03和C++11还不知何在的年代.看完第二章之后合上书,想自己写一个a ...

  4. C/C++编程心得(三)

    各类ptr auto_ptr:它允许程序员创建一个指向某种资源的指针对象,当该对象离开它的作用域时,它所指向的资源也会被自动释放. 在原本的C++中,new和delete必须配对使用,然而给每个异常处 ...

  5. error C2039: “type”: 不是“cv::DataTypeT”的成员 解决办法

    编译opencv4.0.x版本的程序时,由于个人疏忽致使opencv 函数参数顺序调用错误,但是编译时没有提示该语句参数顺序错误,提示以下错误: g:\library\opencv-master\bu ...

  6. C++模板-Traits

    转自:http://blog.csdn.net/my_business/article/details/7891687 介绍traits的文章很多,但感觉大部分文章的说明都很晦涩难懂,把一个并不很复杂 ...

  7. C++ 模板类型萃取技术 traits

    当函数,类或者一些封装的通用算法中的某些部分会因为数据类型不同而导致处理或逻辑不同(而我们又不希望因为数据类型的差异而修改算法本身的封装时),traits会是一种很好的解决方案.(类型测试发生在编译期 ...

  8. [C++基础]034_C++模板编程里的主版本模板类、全特化、偏特化(C++ Type Traits)

    http://www.cnblogs.com/alephsoul-alephsoul/archive/2012/10/18/2728753.html 1.  主版本模板类 首先我们来看一段初学者都能看 ...

  9. 【effective c++读书笔记】【第7章】模板和泛型编程(3)

    条款46:需要类型转换时请为模板定义非成员函数 对条款24的例子进行模板化: #include<iostream> using namespace std;template<type ...

最新文章

  1. Ubuntu常用命令大全[显示桌面]
  2. Android启动画面实现
  3. Openstack部署工具
  4. 《 郝斌 java自学视频 完结 》_《[郝斌]Java自学视频[完结]》AVI[压缩包]
  5. [导入]失败的软件实训课
  6. Django 关联管理器
  7. http通信协议的基本原理
  8. virtualbox中Centos6.6和windows共享文件夹设置
  9. BP: 通过SAP name拿到central person id
  10. 《深入理解OSGi:Equinox原理、应用与最佳实践》一2.2 Bundle
  11. 【原创】Git删除暂存区或版本库中的文件
  12. win10安装oracle12c注意事项,win10系统安装oracle 12c出现ins-30131错误的修复办法
  13. PHP字符编码问题之GB2312 VS UTF-8
  14. 卡尔曼滤波原理理解(1)
  15. 未经授权的ADB Android设备
  16. 微信小程序中引入 UI 组件库(Vant Weapp)
  17. Linux之ES集群搭建
  18. 黄渤海浅层气的分布及特征综述
  19. xx学院学员评优评奖管理系统
  20. python绘制登陆时的卫星云图(TBB)

热门文章

  1. String ua = request.getHeader(user-agent)---ua值为null
  2. HDU 5869 Different GCD Subarray Query 树状数组 + 一些数学背景
  3. WPF利用动画实现圆形进度条
  4. 502 Bad Gateway nginx 解决
  5. 给ButtonBar组件设置自定义TOOLTIP。
  6. 彻底清除计算机远程桌面连接的历史记录
  7. C#中Socket通信用法实例详解
  8. MFC中动态创建button及添加响应事件
  9. c++中用new和不用new创建对象的本质区别
  10. 排除网络故障课后习题参考答案