《STL源码解析》是侯杰大师翻译的著作,其中在Iterator一章着重介绍了traits技巧,认为traits技巧是搞懂的STL源码的入门钥匙,既然编写STL的神人们都这么重视traits,那么traits到底能帮助我们解决什么问题呢?traits的作用在于能“提取”出类型的特性。
举个例子:有个需求是这样的,需要写一个全局的print函数,来打印入参的对象,假设用OO的思想:设计一个cprint的基类,在此基类中用虚函数print,然后每个类型都继承cprint基类,并重写print,那么全局函数就可以这么写:
void print(const cprint& _p)
{
_p.print();
}
很完美吧?哈哈,自己都看着得意,那么问题来了我需要print的类型是原生指针怎么办呢,OO的思想可以实现,但是需要写个包装类,试着换个角度看这个问题吧,来用traits技巧来解决看看:
首先声明两个结构体,没有任何东西,只为了标志,我们的程序世界就靠它们来为我们区分谁有print,谁木有print了。
struct _type_true {};
struct _type_false {};
//接下来这个是一个测试类,其有print函数。
struct student
{
unsigned intid;
unsigned intage;
char name[128];
void print(void) const
{
std::cout << "id:"<< id << "\t"<< "age:" << age << "\t"<< "name:" << name << std::endl;
};
};
// OK,下面最厉害的traits就要出场了,默认的用_type_true来标记,再用偏特化(partial specialization)的方法声明原生指针是_type_false,木有print的
template<typename T>
struct print_traits
{
typedef _type_truehas_print;
};
template<>
struct print_traits <int>
{
typedef _type_falsehas_print;
};
//接下来就是全局的print函数了:通过print_traits的提取,区别调用_print(T *_p, _type_true)和_print(T *_p, _type_false)。
template<typename T>
void print(T _P)
{
typedef typenameprint_traits<T>::has_print has_print;
_print(_P,has_print());
}
template <typename T>
inline void _print(T _P, _type_true)
{
_P.print();
}
template <typename T>
inline void _print(T _P, _type_false)
{
std::cout<< "我是没有Print的,最后尝试下<<操作符吧:" << _P << std::endl;
}
//测试下哈
int main(int argc,char* argv[])
{
student s1;
s1.id= 0;
s1.age= 19;
strncpy(s1.name,"xia kan",sizeof(s1.name));
print(s1);
int i = 0;
print(i);
return 0;
}
总结下traits的运用方法:
声明标记-》运用标记,区分类别(traits)-》设计接口函数和不同类型的重载函数-》利用编译器的调用判定来决定调用哪个函数
traits是编译期多态的一个好应用~!从网上摘了这段话,很能说明问题:
“ traits技巧对类型做了什么?有什么作用?类型和类型的特性本是耦合在一起,通过traits技巧就可以将两者解耦。从某种意思上说traits方法也是对类型的特性做了泛化的工作,通过traits提供的类型特性是泛化的类型特性。从算法使用traits角度看,使用某一泛型类型的算法不必关注具体的类型特性(关注的是泛化的类型特性,即通过traits提供的类型特性)就可以做出正确的算法过程操作;从可扩展角度看,增加或修改新的类型不影响其它的代码,但如果是在type_traits类中增加或修改类型特性对其它代码有极大的影响;从效率方法看,使用type_traits的算法多态性选择是在编译时决定的,比起在运行时决定效率更好。
”