Effective C++笔记 (7.模板与泛型编程)

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

Templates及泛型编程的世界,与面向对象有根本上的不同。在此世界中显示接口和运行期多态仍然存在,但重要性降低。反倒是隐式接口和编译期多态移到前面了。

加诸于template参数身上的隐式接口,就像加诸于class对象身上的显示接口一样真实,而且两者都在编译期完成检查。就像你无法以一种“与class提供之显示接口矛盾”的方式来使用对象(代码将通不过编译),你也无法在template中使用“不支持template所要求之隐式接口”的对象。

请记住:
  • classes和templates都支持接口(interfaces)和多态(polymorphism)。
  • 对classes而言接口是显示的(explicit),以函数签名为中心。多态则是通过virtual函数发生于运行期。
  • 对template参数而言,接口是隐式的(implicit),奠基于有效表达式。多态则通过template具现化和函数重载解析(function overloading resolution)发生于编译期。

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

当声明template类型函数,class和typename的意义完全相同。

template内出现的名称如果相依于某个template参数,称之为从属名称(dependent names)。如果从属名称在class内呈嵌套状,称它为嵌套从属名称(nested dependent name)。

一般性规则很简单:任何时候当你想要在template中指涉一个嵌套从属类型名称,就必须在紧临它的前一个位置放上关键字typename。

请记住:
  • 声明template参数时,前缀关键字class和typename可互换。
  • 请使用关键字typenname标识嵌套从属类型名称;但不得在base class lists(基类列)或member initialization list(成员初值列)内以它作为base class修饰符。

条款43:学习处理模板化基类内的名称

请记住:
  • 可在derived class templates内通过 “this->”指涉base class templates内的成员名称,或籍由一个明白写出的“base class资格修饰符”完成。

条款44:将与参数无关的代码抽离templates

请记住:
  • Templates生成多个classes和多个函数,所以任何template代码都不该与某个造成膨胀的template参数产生相依关系。
  • 因非类型模板参数(non-type template parameters)而造成的代码膨胀,往往可消除,做法是以函数或class成员变量替换template参数。
  • 因类型参数(type parameters)而造成的代码膨胀,往往可降低,做法是让带有完全相同二进制表述(binary representation)的具现类型(instantiation types)共享实现码。

条款45:运用成员函数模板接受所有兼容类型

member function templates(成员函数模板)是个奇妙的东西,但它们并不改变语言基本规则。而语言规则说,如果程序需要一个copy构造函数,你却没有声明它,编译器会为你暗自生成一个。在class内声明泛化copy构造函数(是个member template)并不会阻止编译器生成它们自己的copy构造函数(是个non-template),所以如果你想要控制copy构造的方方面面,你必须同时声明泛化copy构造函数和“正常的”copy构造函数。

请记住:
  • 请使用member function templates(成员函数模板)生成“可接受所有兼容类型”的函数。
  • 如果你声明member templates用于“泛化copy构造”或“泛化assignment操作”,你还需要声明正常的copy构造函数和copy assignment操作符。

条款46:需要类型转换时请为模板定义非成员函数

请记住:
  • 当我们编写一个class template,而它所提供之”与此template相关的“函数支持"所有参数之隐式转换",请将那些函数定义为”class template内部的friend函数“。

条款47:请使用traits classes表现类型信息

Input迭代器:只能向前移动,一次一步,客户只读取(不能涂写)它们所指的东西,而且只能读取一次。它们模仿指向输入文件的阅读指针(read pointer)。

Output迭代器类似:但一切只为输出,向前移动,一次一步,客户只可涂写他们所指的东西,而且只能涂写一次。他们模仿指向输出文件的涂写指针(write pointer)。

forward迭代器:这种迭代器可以做前述两种分类所能做的每一件事,而且可以都或写其所指物一次以上。使得他们可施行于多次性操作算法(multi-pass algorithms)。

Bidrectional迭代器:比上一个分类威力更大,除了可以向前移动,还可以向后移动。STL的list迭代器就属于这一分类,set,multiset,map和multimap的迭代器也都是这一分类。

random access迭代器:比上一个分类威力更大的地方在于它可以执行“迭代器算术”,也就是它可以在常量时间内向前或向后跳跃任何距离。vector,deque和string提供的迭代器都是这一分类。

如何设计并实现一个traits class:
  • 确认若干你希望将来可取得的类型相关信息。例如对迭代器而言,我们希望将来可取其分类(category)。
  • 为该信息选择一个名称。
  • 提供一个template和一组特化版本,内含你希望支持的类型相关信息。

如何使用一个traits class:
  • 建立一组重载函数或函数模板,彼此间的差异只在于各自traits参数。令每个函数实现码与其接受之traits信息响应和。
  • 建立一个控制函数或函数模板。它调用上述那些“劳工函数”并传递traits class所提供的信息。

请记住:
  1. Traits classes使得“类型相关信息”在编译期可用。它们以templates和“templates特化”完成实现。
  2. 整合重载技术(overloading)后,traits classes有可能在编译期对类型执行if...else测试。

条款48:认识template元编程

Template metaprogramming(TMP, 模板元编程)是编写template-based C++程序并执行于编译期的过程。

TMP用途:
  • 确保量度单位正确。
  • 优化矩阵运算。
  • 可以生成客户定制之设计模式(custom design pattern)实现品。

请记住:
  • Template metaprogramming(TMP, 模板元编程)可将工作由运行期移往编译期,因而得以实现早期错误侦测和更高的执行效率。
  • TMP可被用来生成“基于政策选择组合”(based on combinations of policy choices)的客户定制代码,也可用来避免生成某些特殊类型并不适合的代码。

你可能感兴趣的:(C++)