面向对象编程世界总是以显式接口和运行期多态解决问题,如下代码举例说明问题,
class Widget {
public:
Widget();
virtual ~Widget();
virtual std::size_t size()const;
virtual void normalize();
void swap(Widget& other);
...
};
void doProcessing(Widget& w) {
if (w.size() > 10 && w != someNastyWidget) {
Widget temp(w);
temp.normalize();
temp.swap(w);
}
}
显式接口:由于上述代码w的类型被声明为Widget,所以w必须支持Widget接口。如w.size()的成员函数的调用,又如,temp.normalize()和temp.swap()调用。
显示接口由函数的签名式(函数名称、参数类型、返回类型)构成。
运行期多态:由于Widget的某些成员函数是virtual,w对那些函数的调用将表现出运行期多态。也即将于运行期根据w的动态类型决定究竟调用哪一个函数。(类比于哪一个virtual函数该被绑定)
template
void doProcessing(T& w) {
if (w.size() > 10 && w != someNastyWidget) {
T temp(w);
temp.normalize();
temp.swap(w);
}
}
隐式接口:w必须支持哪一种接口,系由template中执行于w身上的操作来决定。但需要注意的是,由于代码中调用了.size() .normalize() .swap()成员函数,因此默认类型T必须支持。
编译期多态:以不同的template参数具现化function templates会导致调用不同的函数。(类比于哪 个重载函数该被调用)
需要值得注意:加诸于template参数身上的隐式接口,就像加诸于class对象身上的显式接口一样真实,而且两者都在编译期完成检查。就像无法以一种“与class提供的显式接口相矛盾”的方式来使用对像(代码将通不过编译),也无法在template中使用“不支持template所要求的隐式接口”的对象(代码一样将通不过编译)。
当声明template类型参数时,class与typename两没有区别。
为了更好说明需要使用typename场合,通过引入相关概念并举例反推,
template
void print2nd(const C& container) {
if (container.size() >= 2) {
C::const_iterator iter(container.begin());//取得第一元素的迭代器
++iter;
int value = *iter;
std::cout << value;
}
}
从属名称:template内出现的名称相依于某个template参数;
嵌套从属名称:从属名称在class内呈嵌套状,如C::const_iterator;
嵌套从属类型名称:嵌套从属名称并且指涉某类型,如C::const_iterator;
非从属名称:顾名思义;
上述代码愚蠢的地方,if语句内我们无形中默认C::const_iterator是个类型,但对于template来说不正确。如果C::const_iterator不是个类型,而恰巧是C有个static成员变量被命名为const_iterator(这种情况也不是不可能)。此时,用成员变量对声明另一个变量简直是天大笑话。
解决上述愚蠢代码的办法,C++有个规则可以解析此一歧义状态:如果解析器在template中遭遇一个嵌套从属名称,它便假设这名称不是个类型,除非你告诉它是(用法:在紧临位置放置关键字typename即可,这就告诉编译器此时嵌套从属名称为类型),此规则有小小例外情况。
template
void print2nd(const C& container) {
if (container.size() >= 2) {
typename C::const_iterator iter(container.begin());//通过typename关键字,声明C::const_iterator是一个类型
++iter;
int value = *iter;
std::cout << value;
}
}
上述
规则的例外是,typename不可以出现在base classes list内的嵌套从属类型名称之前,也不可在member initialization list(成员初值列)中作为base class修饰符(因为在此两区域内,可默认视其一定为类型),如下代码所示,
template
class Derived :public Base::Nested {//base list中不允许使用typename,当然此处默认Base是类型名啦
public:
explicit Derived(int x):
Base::Nested(x) //在member initialization list中也不允许使用typename,当然此处也默认Base是类型名啦
{
typename Base::Nested temp;//既不在base class list中也不在member initialization list中,所以要使用typename
...
}
...
};
以上内容均来自Scott Meyers大师所著Effective C++ version3,如有错误地方,欢迎指正!相互学习,促进!!