0%

模板:模板、泛型编程和静态多态

2020年3月16日 下午9:18

模板的理解

模板最出发点,其实可以从模板两个字上看出,其实就是代码复用,只不过后来有发展出来了编译期计算等复杂的功能

其实就是一个知识:

模板、泛型编程和静态多态

代码复用有几种方式

  • 函数:最原始的
  • 多态:面向对象中的
  • 模板:编译期的

编译期功能的组成部分:

  • 编译期常量:用constexpr声明
  • 编译期计算:类型推导来计算
  • 编译期潜规则:模板匹配规则SFINAE

编译期可以实现哪些功能:

  • 计算:已经证明是图灵完全的
  • 类型检查:对自己定义的类、容器类进行类型检查,是否包含指定函数
    • 这个是运行期无法完成的!这能在编译期完成
  • 代码的复用:
    • 利用模板,完成编译期多态,也就是静态多态
    • eg:虽然 C++ 的标准容器没有对象继承关系,但彼此之间有着很多的同构性。这些同构性很难用继承体系来表达,也完全不必要用继承来表达。C++ 的模板,已经足够表达这些鸭子类型。

函数模板:杂交出来的!或者说是分工合作

  • 编译期会处理一部分,运行期也会处理一部分
  • 编译期主要处理类型,运行期主要处理计算
  • 不杂交不行:因为运行期无法处理类型

函数与模板的合作方式:

  • 函数模板
    • 编译期会处理一部分,运行期也会处理一部分
    • 编译期主要处理类型,运行期主要处理计算
    • 不杂交不行:因为运行期无法处理类型
  • 函数对象、lambda表达式 与 一些C++自带模板进行组合使用

函数与struct模板的区别

  • 执行期不同:
    • 函数在运行期执行
    • 模板在编译器运行
  • 处理对象不同:
    • 函数的处理对象:也就是函数的输入,只能是数据,包括以包含数据的容器、普通类型遍历
    • 模板的处理对象:类型(容器类型,自定义类型) + static const 常量。不是数据
  • body内容不同:
    • 函数体内:定义的是数据运算规则
      • 函数的编译过程会报错
    • struct模板体内:定义的是类型推导规则
      • 模板的编译过程由于有SFINAE不会报错
  • 将编译过程分为两个步骤:先模板编译,后函数编译

泛型的好处:

  1. 可以统一数据类型,便于操作。
  2. 将运行时的异常提前到了编译时,提高了效率。
  3. 避免了强制类型转换
  4. 实现代码的模板化,把数据类型当作参数传递,提高了可重用性。

While< Sum<2>::type >::type::value 实例化(instantiation)过程

  • 对于模板,就是要在脑子里或纸上、电脑上把它展开……☺️
  • 把计算转变成类型推导:在类型推导的时候,可以进行计算,类似于传递参数的时候+1 这样的操作。
  • 模板元编程,其本质是把计算过程用编译期的类型推导和类型匹配表达出来。
    • 计算功能由类型推导完成
    • 类型匹配是模板的天生能力,其实就是多态,只不过是静态多态
  • 在展开的过程中,甚至可以变成递归,这一点一定要理解。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    —> While< SumLoop<0, 2> >::type::value
    —> WhileLoop<SumLoop<0, 2>::cond_value, SumLoop<0, 2>>::type::value
    --> WhileLoop<true, SumLoop<0, 2>>::type::value

    --> WhileLoop<SumLoop<0, 2>::cond_value, SumLoop<0, 2>::next_type>::type::value
    --> WhileLoop<true, SumLoop<2, 1>>::type::value

    --> WhileLoop<SumLoop<2, 1>::cond_value, SumLoop<2, 1>::next_type>::type::value
    --> WhileLoop<true, SumLoop<3, 0>>::type::value

    --> WhileLoop<SumLoop<3, 0>::cond_value, SumLoop<3, 0>::next_type>::type::value
    --> WhileLoop<false, SumLoop<3, -1>>::type::value

    --> SumLoop<3, -1>::res_type::value

    -->integral_constant<int, 3>::value
    -->3