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类型。

模板:什么是Traits的更多相关文章

  1. 对C++ templates类模板的几点补充(Traits类模板特化)

    前一篇文章<浅谈C++ templates 函数模板.类模板以及非类型模板参数>简单的介绍了什么是函数模板(这个最简单),类模板以及非类型模板参数.本文对类模板再做几点补充. 文章目录1. ...

  2. C++ template —— trait与policy类(七)

    第15章 trait与policy类---------------------------------------------------------------------------------- ...

  3. C++程序风格的思考

    转载自:http://www.cppblog.com/weiym/archive/2013/04/27/199781.html 发现厚积薄发中有很多值得学习的东西 故引用之: 最近有机会看号称是公司最 ...

  4. Go语言的成功也预示着Rust的成功【转】

    从整体的角度来看Go,很难理解他是怎么取得这么大的成功的.从理论的角度上来说Go是一门非常糟糕的语言,就算是和C++或者Ada之类旧语言相比也是这样. 从整体的角度来看Go,很难理解他是怎么取得这么大 ...

  5. [转]Traits 编程技法+模板偏特化+template参数推导+内嵌型别编程技巧

    STL中,traits编程技法得到了很大的应用,了解这个,才能一窥STL奥妙所在. 先将自己所理解的记录如下: Traits技术可以用来获得一个 类型 的相关信息的. 首先假如有以下一个泛型的迭代器类 ...

  6. 模板的Traits

    Traits含义就是特性,应用Trait模板参数,使得我们的程序既保持灵活性,同时减少类型参数的数量.能够使得我们对函数进行更加细粒度的控制. #ifndef TRAIT_H_ #define TRA ...

  7. 【C++模版之旅】项目中一次活用C++模板(traits)的经历

    曾经曾在一个项目中碰到过一个挺简单的问题,但一时又不能用普通常规的方法去非常好的解决,最后通过C++模板的活用,通过traits相对照较巧妙的攻克了这个问题.本文主要想重现问题发生,若干解决方式的比較 ...

  8. c/c++ 模板与STL小例子系列<三> traits

    c/c++ 模板与STL小例子系列 traits 对这个概念,还是处于懵逼的状态,初步体会就是,为了解决类型之间的转换问题. 从一个类型为A的指针,转化到类型为B的指针,中间需要用void*来作为中介 ...

  9. 【C++模版之旅】项目中一次活用C++模板(traits)的经历 -新注解

    问题与需求: 请读者先看这篇文章,[C++模版之旅]项目中一次活用C++模板(traits)的经历. 对于此篇文章提出的问题,我给出一个新的思路. talking is cheap,show me t ...

随机推荐

  1. OC学习——OC中的@protocol(@required、@optional)、代理设计模式

    一.什么是协议? 1.协议声明了可以被任何类实现的方法   2.协议不是类,它是定义了一个其他对象可以实现的接口   3.如果在某个类中实现了协议中的某个方法,也就是这个类实现了那个协议.   4.协 ...

  2. python 九九乘法表

    for i in range(1,10): for j in range(1,i+1): print('%d x %d = %d\t' %(j, i, j*i),end="") p ...

  3. hdu 1411(四面体的体积)

    校庆神秘建筑 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Subm ...

  4. MAC使用homeBrew安装Redis

    homeBrew的操作命令如下: brew search ** //查找某个软件包 brew list //列出已经安装的软件的包 brew install ** //安装某个软件包,默认安装的是稳定 ...

  5. Linux上安装Redis教程

    Redis的安装步骤: 步骤1.安装redis必须已经安装了gcc,如果没安装gcc 就使用命令 yum install -y gcc步骤2.下载redis包 下载地址:http://download ...

  6. Codeforces 1023 D.Array Restoration-RMQ(ST)区间查询最值 (Codeforces Round #504 (rated, Div. 1 + Div. 2, based on VK Cup 2018 Fi)

    D. Array Restoration 这题想一下就会发现是只要两个相同的数之间没有比它小的就可以,就是保存一下数第一次出现和最后一次出现的位置,然后查询一下这个区间就可以,如果有0的话就进行填充. ...

  7. NYOJ 6.喷水装置(一)-贪心

    喷水装置(一) 时间限制:3000 ms  |  内存限制:65535 KB 难度:3   描述 现有一块草坪,长为20米,宽为2米,要在横中心线上放置半径为Ri的喷水装置,每个喷水装置的效果都会让以 ...

  8. AtCoder Grand Contest 023 A - Zero-Sum Ranges

    Time limit : 2sec / Memory limit : 256MB Score : 200 points Problem Statement We have an integer seq ...

  9. Codeforces #428 Div2 D

    #428 Div2 D 题意 给出一些数,现在要求找出一些数满足 \(i_1 < i_2 < i_3 < ... < i_k\) 以及 \(gcd(a_{i_1}, a_{i_ ...

  10. kattis Curious Cupid (莫队算法)

    Curious Cupid There are K different languages in the world. Each person speaks one and only one lang ...