条款41:了解隐式接口和编译器多态
(1)classes和template都支持接口和多态,对class而言接口是显式的,以函数签名为中心,多态则是通过virtual函数发生于运行期,对template参数而言,接口是隐式的,奠基于有效表达式,多态则是通过template具现化和函数重载解析发生于编译器。
条款42:了解typename的双重意义
(1)typename和class在template<typename T>这样的用法是相同的
(2)typename还有一个特别的用法,因为使用模版的类是一个不确定的类,如果我们使用了不确定类的不确定成员变量,这种情况我们要在前面加上typename,如
template <typename T> class B { typename T::const_iterator iter; // };因为const_iterator是一个迭代器,我们默认我们以后为T传进去的参数是一个容器,在这种情况下const_iterator就是一个不确定类的不确定成员变量(也称嵌套从属类型),就需要在前面加上typename。但是有两个例外,第一就是typename不可以出现在base class list内的嵌套从属类型名称之前,也不可在成员初值列中作为base class修饰符。
条款43:学习处理模版化基类内的名称
(1)在模版的子类中调用父类的方法会编译不通过,如下例所示:
#include<iostream> using namespace std; class CompanyA { public: void sendCleartext( const string& msg ); void sendEncrypted( const string& msg ); }; class CompanyB { public: void sendCleartext( const string& msg ); void sendEncrypted( const string& msg ); }; ....//针对其他公司设计的classes class MsgInfo{...};//用来保存信息,以备将来产生信息 template<typename Company> class MsgSender { public: void sendClear( const MsgInfo& info ) { string msg; // 在这,根据info产生信息 Company c; c.sendCleartext( msg ); } void sendSecret( const MsgInfo& info ) { ...//类似于sendClear } }; template<typename Company> class LoggingMsgSender:public MsgSender<Company> { public: void sendClearMsg( const MsgInfo& info ) { //将传送前的信息写至log sendClear( info ) ;//错误的代码,可能特化的模版就没有这个函数 //将传送后的信息写至log } };导致这样的结果的原因是,模版存在特化的情况,在LoggingMsgSender中调用父类MsgSender中的方法sendClear的时候会报错,是因为如果父类如果存在特化,并且特化的类中没有这个函数那么就将导致错误,为了防止这种错误的发生,C++编译器就会在编译的时候防止这种事情发生,报错。
为了解决上述问题,C++可以采用3中解决办法,这些解决办法的实质是向编译器承诺该方法是存在的,第一:在base class函数调用动作之前加上“this->”,第二:使用using声明式。如:using MsgSender<Company>::sendClear,第三:明白的指出被调用的函数位于base class内,MsgSender<Company>::sendClear()
条款44:将于参数无关的代码抽离template
(1)template容易导致代码膨胀,而且不易发现,所以为了防止template代码膨胀可以采用将共用的一些方法抽离出来,组成基类。
条款45:运用成员函数模版接受所有兼容类型
(1)