Effective C++之条款41、42

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

    Template及泛型编程的世界,与面向对象有根本上的不同,它存在隐式接口和编译期多态。如下代码:

template<typename T>
void doProcessing(T& w){
	if (w.size() > 10 && w != someNastyWidget) {
		T temp(w);
		temp.normalize();
		temp.swap(w);
	}
}
  • 本例中,w必须支持size,normalize和swap成员函数、copy构造函数、不等比较。这一组表达式(对template而言必须有效编译)便是T必须支持的一组隐式接口。
  • 凡涉及w的任何函数调用,例如operate>和operate!=,有可能造成template具现化,使这些调用得以成功。这样的具现行为发生在编译期。“”以不同的template参数具现化function templates”会导致调用不同的函数,这便是所谓的编译期多态。

    显示接口由函数的签名式(函数名称、参数类型、返回类型)构成。隐式接口就完全不同了。他是由有效的表达式组成:

请记住

  • classes和templates都支持接口和多态。
  • 对classes而言接口是显示的,一函数签名为中心,多态则是通过virtual函数发生于运行期。
  • 对template参数而言,接口是隐式的,奠基于有效表达式。多态则是通过template具现化和函数重载解析发生于编译期。

条款42:了解typename的双重意义

    对于template声明式中,class和typename有什么不同?

template<class T> class Widget;
template<typename T> class Widget;

    其实两者并没有什么不同,当我们声明template类型参数,class和typename的意义完全相同。

    然而C++并不总是把class和typename视为等价。

template<typename C>
void print2nd(const C& container)
{
	if(container.size() >= 2){
		C::const_iterator iter(container.begin());
	}
}

    如果解析器在template中遭遇一个嵌套从属名称,它便假设这个名称不是个类型,除非你告诉它是。所以缺省情况下嵌套从属名称不是类型。因此我们需要告诉C++它是类型,此时只需要在紧邻它之前放置关键字typename即可:

typename C::const_iterator iter(container.begin());

    然而在typename不能出现在base class list中以及成员初值列表中作为基类修饰符。

template<typename T>
class Derived: public Base<T>::Nested { //base class list不允许出现typename
public:
	explicit Derived(int x)
		:Base<T>::Nested(x)        //成员初值列表,不允许typename
		{
			typename Base<T>::Nested temp;   //嵌套从属类型名称,此处允许typename
		}
};

    typedef和typename一起使用用来指涉一个名称为成员类型而非成员变量,并为其设定typedef名称(简化):

template<typename IterT>
void workWithIterator(IterT iter)
{
	typedef typename std::iterator_traits<IterT>::value_type value_type; 
	value_type temp(*iter);
}

请记住

  • 声明template参数时,前缀关键字class和typename可互换。
  • 请使用关键字typename标识嵌套从属类型名称:但不得在base class lists及成员初值列内以它作为修饰符。
发布了33 篇原创文章 · 获赞 6 · 访问量 561

猜你喜欢

转载自blog.csdn.net/weixin_43519984/article/details/102964278
今日推荐