模板与泛型程序可以创造出“在C++编译器内执行并于编译器完成时停止执行”的程序。
条款41:了解隐式接口与编译器多态
classes和templates都支持接口和多态。
对classes而言是显示的,以函数签名为中心。多台则是通过virtual函数发生于运行期。
对template参数而言,接口是隐式的,奠基于有效表达式。多态则是通过template具现化和函数重载解析发生于编译期。
以不同的template具现化function templates会导致调用不同的函数,这就是编译期多态。运行期多态是指哪一个virtual被绑定,编译期多态是指哪一个重载还是会被调用。
条款42:了解typename的双重意义
声明template参数时,前缀关键字class和typename可互换。
请使用关键字typename标识嵌套从属类型名称,但不得在base classes lists(基类列表)或member initialization list(成员初始化列表)内使用它作为base class的修饰符。
template内出现的名称如果相依于某个template参数,称之为嵌套从属名称。如果从属名称在类内呈嵌套状。称它为嵌套从属名称。如以下:
//嵌套从属类型
template <typename C>
print2nd(const C& container)
{
//以下是一个嵌套从属名称,因为C是模板参数,并且const_iterator被嵌套于
//这个C中,所以下面需要使用typename,表明它是一个类型
typename C::const_iterator* x;
}
之所以要加是因为C++解析这个有歧义的状态的规则是:如果解析器在template遭遇一个嵌套从属名称,它便假设这名称不是一个类型,除非你告诉他是。即缺省情况下嵌套从属名称不是类型。所以任何使用要在template中指涉一个嵌套从属类型名需要在它前面加typename,而且typename只能用来验明嵌套从属名称,正常类型不应该有他存在,如:
//typename只被用来验明嵌套从属类型名称,其他名称不该有它存在
template <typename C> //允许使用typename与class,此中情况下等同
void f(const C& container, //正常类型,不允许使用typename
typename C::iterator iter); //嵌套类型,需要使用typename
以上这个例外出现在以下这种情况,继承基类成员列表内的嵌套从属类型或在成员初始化列表中作为积累的修饰符,如下:
template <typename T>
class Derived : public Base<T>::Nested //基类列表中不允许
{
public:
explicit Derived(int x)
: Base<T>::Nested(x) //成员初始化列表中不允许
{
typename Base<T>::Nested temp; //嵌套从属类型加typename
}
};
最后一个typename的例子,是STL库中的typename的这种运用:
//STL中typename
template <typename IterT>
void workWithIterator(IterT iter)
{
typename std::iterator_traits<IterT>::value_type temp(*iter);
}
std::iterator_traits<IterT>::value_type是标准traits class的一种运用,意思就是说“类型为IterT之对象所指之物的类型”。语句的意思就是声明一个local变量,使用IterT对象所指物。如果IterT是vector<int>::iterator, temp的类型就是int, 如果是list<string>::iterator,temp类型就是string,以上是个嵌套从属类型,value_type嵌套于iterator_traits<IterT>之内而IterT是个template参数,所以前面必须加typename., 但这种类型太长,使用不方便,可以使用typedef声明一个类型,如下:
template <typename IterT>
void workWithIterator(IterT iter)
{
//为嵌套从属类型使用一个类型声明符,value_type就代表这个类型
typedef typename std::iterator_traits<IterT>::value_type value_type;
value_type temp(*iter);
}
条款43:学习处理模板化基类内的名称
如果一个类继承自一个模板类,则如果在继承类中直接调用基类的共有函数会失败,因为C++知道基类模板有可能被特化,而那个特化的版本可能不提供和一般性template相同的接口,所以它采取的行为是拒绝在templatized base classes(模板化基类)内寻找继承而来的名称。这与OO C++是不同的。为了使继承类进入模板基类内查找名称,有三种方式:
1 在调用函数前使用this->func()
2 使用using声明式,这并不是base class名称被derived class名称遮掩,而是编译器不进去查找,用using告诉他,然后它会进去超找。
3 明确指出被调用的函数位于base class内,如MsgSender<Company>::sendClear(info),但这种情况如果被调用的是virtual函数,则这种调用方式会关闭”virtual绑定行为“。
条款44:将参数无关的代码抽离template
Templates生成多个classes和多个函数,所以任何template代码都不该与某个造成膨胀的template参数产生相依关系
因非类型模板参数而造成的代码膨胀,往往可以消除,做法是以函数参数或class成员变量替换template参数。
因类型参数而造成的代码膨胀,往往可以降低,做法是让带有完全相同的二进制表述的具体类型共享实现码。
条款45:运用成员函数模板接受所有兼容类型
请使用member function template生成“可接受所有兼容类型”的函数
如果你声明member template用于“泛化copy构造”或"泛化assignment操作",你还是需要声明正常的copy构造函数和copy assignment操作符。
真实指针支持隐式转换,Derived类指针可以隐式转换为基类指针,指向non-const对象的指针可以转换为“指向const对象“等等。
条款46:需要类型转换时请为模板定义非成员函数
在一个class template内,template名称可被用过来作为“template和其参数”的简略表达方式。所以Rational<T>可以只写Rational而不必写Rational<T>。
因为模板这块学的一般,就先简单总结下吧,以后用到可以再看~~