C++编程规范:模板与泛型

第64条:理智地结合静态多态性和动态多态性

  1. 多态性意味着一个给定值能够有一种以上的类型,而给定函数能够接受类型与其形参不符的实参。 多态性的优势在于,同一段代码能够操作于不同类型,甚至可以是在编写代码时不知道的类型
  2. 动态多态性是以某些类的形式出现的,这些类含有虚拟函数和(通过指针或引用)间接操作的实例 静态多样性则与模板类和模板函数有关。
  3. 在C++中多态多态性最擅长于以下几个方面:
    • 基于超集/子集关系的统一操作:具有超集/子集关系的不同类可以被统一处理
    • 静态类型检查
    • 动态绑定和分别编译:使用层次结构中类的代码与整个层次结构的代码时分别编译的
    • 二进制接口:既可以静态链接模块,又可以动态链接模块,只要所链接模块的虚拟表布局方式相同。
  4. 静态多态性最擅长以下几个方面:
    • 基于语法和语义接口的统一操作
    • 静态类型检查
    • 静态绑定
    • 效率:编译时估值和静态绑定能够带来动态绑定所不具备的优化和效率
  5. 可辨识联合,也称标签联合(tagged union)或者变体类型(variant type),指的是能够存储一组不同但是固定的类型中某个类型的对象,具体是哪个类型由标签字段决定

    第65条:有意地进行显式自定义

  6. 在编写模板时,应该有意地、正确地提供自定义点,并清晰地记入文档。
  7. 自定义点:在这些点,调用者的代码能够在模板中被查到并使用。只需常规地调用另一个函数或操作符(无限定的),如果其参数之一碰巧就是模板参数类型(或者相关的类型),那么ADL就会选中它。
  8. 提供自定义点的三种主要方式:
    • “隐式接口”方法,其中模板直接依赖于类型具有给定名字的合适成员
      template<typename T>
      void Sample1(T t){
       t.foo();    //调用含有成员符号的函数
       typename T::value_type x; //提供自定义点来查找类型
      }
      
    • 采用通过ADL查找的非成员函数,依赖的是 类型具有给定名字的合适的非成员
      template<typename T>
      void Sample2(T t){
      foo();    //foo是一个调用非成员函数的自定义点
      cout<<t;  //带有operator符号的operator<<
      }
      
    • 依赖于已经特化了另一个类模板
      template<typename T>
      void Sample3(T t){
      S3Traits<T>::foo(t);  //S3Traits<>::foo是一个自定义点
      }
      

      如果自定义点对于内置类型也是可自定义的,那么应该选择2或3,如果确实是类型提供服务的那些常见操作,那么应该选择1或者2

  9. 避免无意地提供自定义点,应该做到:
    • 将模板内部使用的任何辅助函数都放入其自己的内嵌名字空间,并用显式的限定调用它们以禁用ADL。 在需要调用自己的辅助函数并传递一个模板参数类型的对象,而且该调用不是一个自定义点时,应该将辅助函数放入内嵌名字空间,并通过限定调用或者将函数名放入括号,来显式地关闭ADL。
      template<typename T>
      void Sample4(T t){
       S4Helper::bar(t);   //禁用ADL 
       (bar)(t);   //禁用ADL
      }
      
    • 要避免依靠依赖名(依赖于模板参数,构造的语义会随着不同的实例化而变化)。在引用依赖基类的任何成员时,应该总是用基类名或者this->显式地进行限定 ``` c++ template class C:X{ typename X::SomeType s; //使用基类的内嵌类型或者typedef

public: void f(){ X::baz(); //调用基类成员函数 this->bax(); //this->显式地进行调用 } } ```

  1. 为模板提供显式的自定义点,或者避免无意的自定义点,其本质都是C++名字查找规则,就是利用C++的名字查找规则,促进有意的名字查找,避免无意的名字查找。

    第66条:不要特化函数模板

  2. 在扩展其他人的函数模板时,要避免尝试编写特化代码;相反要编写函数模板的重载,将其放在与重载所用类型的名字空间中。
  3. 重载解析会考虑所有可见的模板,而且编译器将只是选择最佳的匹配 特化函数模板很不直观,有两个原因:
    • 不可能部分地特化函数模板,只能完全特化。
    • 函数模板特化决不能参与重载。所编写的任何特化对使用哪个模板都将毫无影响。如果要编写一个签名完全相同的非模板函数,而不是函数模板特化的话,所选中的将总是非模板函数,因为与模板相比,它总是被认为是最佳的选择。
  4. 如果要实现一个函数模板,应该将它编写成一个永远都不会被特化或者被重载的函数模板,并使用一个类模板来实现函数模板。

    第67条:不要无意地编写不通用的C代码

    使用最通用、最抽象的方法来实现一个功能。

Table of Contents