traits技术

为什么要用traits?

traits英语意思是特性,特点。c++中使用traits技术动机一般有三种,分派、效率、使某些代码通过编译。

分派

下面有一个模板函数,假设一个动物收容组织提供了它,他们接受所有无家可归的可怜的小动物,于是他们向外界提供了一个函数接受注册。函数看起来像这样:

template<class T> // T表示接受的是何种动物

void AcceptAnimals(T animal)
{
...  //do something
};

但是,如果他们想将猫和狗分开处理(毕竟饲养一只猫和饲养一只狗并不相同。他们可能会为狗买一根链子,而温顺的猫则可能不需要)。一个可行的方法是分别提供两个函数:AcceptDog和AcceptCat,然而这种解决办法并不优雅(想想看,注册者可能既有一只猫又有一只狗,这样他不得不调用不同的函数来注册,而且,如果种类还在增多呢,那样会导致向外提供的接口的增多,注册者因此而不得不记住那些烦琐的名字,而这显然没有只需记住AccpetAnimal这一个名字简单)。如果想保持这个模板函数,并将它作为向外界提供的唯一接口,则我们需要某种方式来获取类型T的特征(trait),并按照不同的特征来采用不同的策略。这里我们有第二个解决办法:

约定所有的动物类(如class Cat,class Dog)都必须在内部typedef一个表明自己身份的类型,作为标识的类型如下:

struct cat_tag{}; //这只是个空类,目的是激发函数重载,后面会解释
struct dog_tag{}; //同上

于是,所有狗类都必须像这样:

class Dog
{
public:
    // 类型(身份)标志,表示这是狗类,如果是猫类则为typedef cat_tag type;
    typedef  dog_tag  type; 
  ...
}


然后,动物收容组织可以在内部提供对猫狗分开处理的函数,像这样:


// 第二个参数为无名参数,只是为了激发函数重载

template<class T>
void Accept(T dog,dog_tag) 
{...}

template<class T>
void Accpet(T cat,cat_tag) // 同上
{...}


于是先前的Accept函数可以改写如下:

template<class T>
void Accept(T animal)  //这是向外界提供的唯一接口
{
// 如果T为狗类,则typename T::type就是dog_tag,那么typename T::type()就是创建了一个dog_tag类的临时对象,根据函数重载的规则,这将调用Accept(T,dog_tag),这正是转向处理狗的策略。如果T为猫类,则typename T::type为cat_tag,由上面的推导,这将调用Accept(T,cat_tag),即转向处理猫的策略,typename 关键字告诉编译器T::type是个类型而不是静态成员。

Accept(animal, typename T::type()); // #1
}

所有类型推导,函数重载,都在编译期完成,你几乎不用耗费任何运行期成本(除了创建dog_tag,cat_tag临时对象的成本,然而经过编译器的优化,这种成本可能也会消失)就拥有了可读性和可维护性高的代码。“但是,等等!”你说:“traits在哪?”,typename T::type其实就是traits,只不过少了一层封装而已,如果像这样作一些改进:

template<typename T>
struct AnimalTraits
{
typedef T::type type;
};

于是,#1处的代码便可以写成:

Accept(animal, typename AnimalTraits<T>::type());

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值