Effective C++ 条款41、42

条款41 了解隐式接口和编译期多态

面向对象编程世界总是以显式接口和运行期多态解决问题,如下代码举例说明问题,

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<typename T>
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所要求的隐式接口”的对象(代码一样将通不过编译)。

条款42 了解typename的双重意义

当声明template类型参数时,class与typename两没有区别。

为了更好说明需要使用typename场合,通过引入相关概念并举例反推,

template<typename C>
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<typename C>
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<typename T>
class Derived :public Base<T>::Nested {//base list中不允许使用typename,当然此处默认Base<T>是类型名啦
public:
	explicit Derived(int x):
		Base<T>::Nested(x) //在member initialization list中也不允许使用typename,当然此处也默认Base<T>是类型名啦
	{
		typename Base<T>::Nested temp;//既不在base class list中也不在member initialization list中,所以要使用typename
		...
	}
	...
};

以上内容均来自Scott Meyers大师所著Effective C++ version3,如有错误地方,欢迎指正!相互学习,促进!!

猜你喜欢

转载自blog.csdn.net/tt_love9527/article/details/80835150
今日推荐