[原创文章欢迎转载,但请保留作者信息]
Justin 于 2010-05-11
看到题目我笑了,原来traits也被大师归为55式不外传神功之一。看来当初花的时间还是值得的啊@#¥%
貌似谈到traits都要扯上STL、Boost,大师这回也未能免俗。开课就先介绍5种迭代器,input_iterator, output_iterator, forward_interator, bidirectional_iterator 和random_access_iterator。不过我不想再走老路了,还是自己杜撰些东东来比较好玩。
大师说:traits首先应该是个结构体(struct),然后是个被当作类来用的结构体,因为用了公有继承来表示以上5种迭代器的关系(@#$%)
Traits 也叫“Baggage Class”(Nathan C. Myers, C++ Report, June 1995)。在我看来,traits可以理解为类型信息榨取机,制造它的过程大致分以下几步,这里来个简单的例子,我们以不同食物的好吃/不好吃作为食物的类型:
1、既然和类型信息有关,首先需要知道所有可能的类型(如果是void,int之类的已经类型,就可以忽略此步骤),我们的食物有一下两种类型:
struct yummy {};
struct yucky {};
2、用一个模板化的traits结构体来构造用于榨取类型信息的“吸管”:
template <typename T>
struct food_traits
{
typedef typename T::Taste taste;
};
上面的“吸管”用来得到某种食物的好吃/不好吃属性。“吸管”能工作的前提是:某食物类一定要有这一属性,要么好吃(yummy)要么不好吃(yucky)。
3、然后根据不同的食物制定不同的模板函数的重载:
template <typename T>
string doTry ()
{
return "well, i don't know what this is but i will till take a try..";
}
template <typename T>
string doTry(T&, yummy)
{
return "of course i like this!";
}
template <typename T>
string doTry (T&, yucky)
{
return "no,keep it away from me..";
}
4、当然,每个“食物”在制作的过程中也要加入好吃/不好吃的元素:
class Cheeseburg
{
public:
typedef yummy Taste;
//..
};
class Wasabi
{
public:
typedef yucky Taste;
//..
};
5、最后……当然就是品尝食物咯~~
int main()
{
Cheeseburg C;
Wasabi W;
cout << "Cheeseburg? :" << doTry(C, food_traits<Cheeseburg>::taste()) << endl;
cout << "Wasabi? :" << doTry(W, food_traits<Wasabi>::taste()) << endl;
return 0;
}
这个例子比较STL或是其他的真实例程来说比较简单明了,而且有我爱吃的芝士堡,希望你也喜欢:)
不过在大师的课堂里,其实还有一步。为了使调用第3步中的重载模板函数更简单,隐藏不必要的细节,我们可以再加上一层wrapper函数:
template <typename T>
string Try(T& food)
{
return doTry(food, typename food_traits<T>::taste());
}
然后上面的调用就简化为:
//..
cout << "Cheeseburg? :" << Try(W) << endl;
cout << "Wasabi? :" << Try(C) << endl;
//..