traits技法简单入门
首先要对元编程有所了解,要知道模板类,如果用过STL就更好了。
我最早是在STL源码剖析一书中看到trait技法的,运用这一技法将本来在runtime时完成的工作转到了编译时期完成。
下面是一个通常用来作示例的,关于元编程的例子,读者自己去体会。
struct fibonacci
{
enum{value=fibonacci<n-1>::value+fibonacci<n-2>::value};
};
template<>
struct fibonacci<1>
{
enum{value=1};
};
template<>
struct fibonacci<0>
{
enum{value=0};
};
下面讲一个trait的应用示例。【1】
template<typename T>
void doSomething(T& t)
{
if(t 具有某种特性)
{
做一些事情
}
else
{
做另一些事情
}
}
对上面这段代码改进到编译期完成:
首先,定义表示‘真’和‘假’的类:
struct _true_type{};
struct _false_type{};
它们对应于运行期的值,true和false;
再将特性定义成内嵌类型的typedef:
class T
{
typedef _true_type copiable;
};
然后在doSomething()里做类型推导:
template<typename T>
void doSomething(T& t)
{
typedef typename T::copiable copiable;
doSomething_aux(t,copiable());
}
真正在推导的是另一个重载的函数doSomething_aux,如下定义
template<typename T>
void doSomething_aux(T& t,_true_type)
{
当可以拷贝时做的事情
}
template<typename T>
void doSomething_aux(T& t,_false_type)
{
当不可以拷贝时做的事情
}
这样就大功告成,似乎只用到了重载函数,但是这样的特性方法不能推广到基本类型,比如,T是int型,那在doSomething里就出错了,int::copiable没有定义,当然啦,你不可能把这个特性加进去。
再加一层封装可以解决这个问题:
template<typename T>
struct type_traits
{
typedef T::copiable copiable;
};
然后改写doSomething:
template<typename T>
void doSomething(T& t)
{
typedef typename type_traits<T>::copiable copiable;
doSomething_aux(t,copiable());
}
为什么要转这么个弯?因为这样做了就有机会为基本类型添加特性信息,我们可以特化这个type_traits为基本类型,如
template<>
struct type_traits<int>
{
typedef _true_type copiable;
};
STL里有个文件type_traits.h里面就定义了所有这些基本类型的偏特化版,当然,这里的copiable是我想出来的一个特性。
不仅仅只能为type可以定义traits,还可以为其它很多概念定义traits,比如iterator,它的偏特化版就牵扯到原始指针,可以认为,原始指针是一种iterator,而iterator是一个抽象的概念。
大致是这样的了。