读书笔记:Effective C++ 炒冷饭 - Item47 traits:类型信息榨取机
[原创文章欢迎转载,但请保留作者信息]
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之类的已经类型,就可以忽略此步骤),我们的食物有一下两种类型:
2、用一个模板化的traits结构体来构造用于榨取类型信息的“吸管”:
上面的“吸管”用来得到某种食物的好吃/不好吃属性。“吸管”能工作的前提是:某食物类一定要有这一属性,要么好吃(yummy)要么不好吃(yucky)。
3、然后根据不同的食物制定不同的模板函数的重载:
4、当然,每个“食物”在制作的过程中也要加入好吃/不好吃的元素:
5、最后……当然就是品尝食物咯~~
这个例子比较STL或是其他的真实例程来说比较简单明了,而且有我爱吃的芝士堡,希望你也喜欢:)
不过在大师的课堂里,其实还有一步。为了使调用第3步中的重载模板函数更简单,隐藏不必要的细节,我们可以再加上一层wrapper函数:
然后上面的调用就简化为:
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 {};
struct yucky {};
2、用一个模板化的traits结构体来构造用于榨取类型信息的“吸管”:
template
<
typename T
>
struct food_traits
{
typedef typename T::Taste taste;
};
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.. " ;
}
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;
// ..
};
{
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 ;
}
{
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());
}
string Try(T & food)
{
return doTry(food, typename food_traits < T > ::taste());
}
然后上面的调用就简化为:
//
..
cout << " Cheeseburg? : " << Try(W) << endl;
cout << " Wasabi? : " << Try(C) << endl;
// ..
cout << " Cheeseburg? : " << Try(W) << endl;
cout << " Wasabi? : " << Try(C) << endl;
// ..