41. 了解隐式接口和编译期多态
41.1 面向对象编程的世界总是以显式接口和运行期多态(polymorphism)解决问题。
41.2 类和模板都支持接口和多态。
接口 | 多态 | |
---|---|---|
类class | 显式的,基于函数签名 | 通过虚函数发生于运行期 |
模板template | 隐式的,基于有效表达式 | 通过template具体实现和函数重载解析发生于编译期 |
42. 了解typename的双重意义
42.1 声明template参数时,<class T>和<typename T>的意义完全相同。
42.2 模板函数里的几种变量名称
变量名称 | 英文 | 定义 |
---|---|---|
从属名称 | dependent names | 依赖于某个template参数 |
嵌套从属名称 | nested dependent names | 从属名称在class内呈嵌套状 |
嵌套从属类型名称 | nested dependent type names | 嵌套从属名称并指向某种类型 |
非从属名称 | non-dependent names | 不依赖于任何template参数 |
缺省情况下解析器认为嵌套从属名称不是个类型,除非你告诉它是(在前面放typename)。
42.3 "typename必须作为嵌套从属类型名称的前缀词"这一规则的例外是,typename不可以出现
在base classes list内的嵌套从属类型名称之前,也不可在member initialization list中作为
base class修饰符。
42.4 使用typedef定义嵌套从属类型名称(如果名字很长)的别名。
42.5 typename在不同编译器上表现不同,可移植性差。
43. 学习处理模板化基类内的名称
43.1 "template<>"表示模板全特化,这既不是template也不是标准class,而是个特化版的具体
某个类的template。一旦类型参数被定义,再没有其它template参数可供变化。
43.2 基类模板可能被特化,而特化版本可能不提供和一般性模板相同的接口。所以编译器拒绝
在模板化基类内寻找继承而来的名称(函数)。
43.3 继承类模板内调用模板化基类函数的三个方法
a. 在基类函数调用动作之前加上"this->"。
b. 使用using声明式,在继承类了声明基类函数。
c. 明白地指出被调用的函数位于基类内(不适合虚函数)。
44. 将与参数无关的代码抽离templates
44.1 Templates生成多个类和多个函数,所以任何template代码都不该与某个造成膨胀的
template参数产生相依关系。注意template代码中,重复是隐晦的。
44.2 因非类型模板参数而造成的代码膨胀,往往可消除,做法是以函数参数或类成员变量替换
template参数。
44.3 因类型参数而造成的代码膨胀,往往可降低,做法是让带有完全相同二进制表述(比如指针
类型)的具现类型共享实现码。
45. 运用成员函数模板接受所有兼容类型
45.1 请使用成员函数模板生成"可接受所有兼容类型"的函数。
45.2 如果声明member templates用于"泛化copy构造"或"泛化assignment操作",你还是需要声
明正常的copy构造函数和copy assignment操作符。
46. 需要类型转换时请为模板定义非成员函数
46.1 在template实参推导过程中从不将隐式类型转换函数纳入考虑。
46.2 template class内的friend声明式可以指涉某个特定函数。使用friend是目的是支持"所有参
数的隐式类型转换"。
46.3 为了让类型转换可能发生于所有实参身上,我们需要一个非成员函数;为了令这个函数被
自动具现化,我们需要将它声明在class内部;而在class内部声明非成员函数的唯一办法
就是令它为friend。这里使用friend与它的传统用途"访问class内非public成分"没有关系。
47. 请使用traits classes表现类型信息
47.1 STL的5种迭代器分类
类型 | 描述 | 代表 |
---|---|---|
Input | 只能向前,一次一步,只读且一次 | istream_iterators |
Output | 只能向前,一次一步,只写且一次 | ostream_iterators |
forward | 可读写一次以上(单向链表),继承自input | slist、tr1::hashed? |
bidirectional | 可向前或向后移动,继承自forward | list、set、map |
random access | 可在常量时间内向前或向后跳跃任意距离,继承自bidirectional | vector、deque、string |
47.2 Traits
Traits并不是C++关键字或一个预先定义好的组件;它们是一种技术,也是一个C++程序员
共同遵守的协议。它对内置类型和用户自定义类型的表现必须一样好。习惯上,traits总是
被实现为structs,但它们往往被称为traits classes。Traits classes使得"类型相关信息"在编
译期可用,以模板和模板特化完成实现。整合重载技术后,traits classes有可能在编译期
对类型执行if...else测试。
47.3 设计并实现一个traits class
a. 确认若干你希望将来可取得的类型相关信息。
b. 为该信息选择一个名称。
c. 提供一个template和一组特化版本,内含你希望支持的类型相关信息。
47.4 如何使用一个traits class
a. 建立一组重载函数(劳工函数)或函数模板,彼此间的差异只在各自的traits参数。令每个函
数实现码与其接受之traits信息相对应。
b. 建立一个控制函数(工头)或函数模板,它调用上述那些"劳工函数"并传递traits class所提供
的信息。
48. 认识template元编程
TMP(模板元编程),是编写基于模板的C++程序并执行于编译期的过程。
48.1 TMP可将工作由运行期移到编译期,因而得以实现早期错误侦测和更高的执行效率(较小的
可执行文件、较短的运行期、较少的内存需求),缺点是编译时间变长了。
48.2 TMP可被用来生成"基于政策选择组合"的客户定制代码(如Strategy、Observer、Visitor),
也可用来避免生成对某些特殊类型并不适合的代码。TMP里的循环是用递归完成的。