2020年3月16日 下午9:18
模板的理解
模板最出发点,其实可以从模板两个字上看出,其实就是代码复用,只不过后来有发展出来了编译期计算等复杂的功能
其实就是一个知识:
模板、泛型编程和静态多态
代码复用有几种方式
- 函数:最原始的
- 多态:面向对象中的
- 模板:编译期的
编译期功能的组成部分:
- 编译期常量:用constexpr声明
- 编译期计算:类型推导来计算
- 编译期潜规则:模板匹配规则SFINAE
编译期可以实现哪些功能:
- 计算:已经证明是图灵完全的
- 类型检查:对自己定义的类、容器类进行类型检查,是否包含指定函数
- 这个是运行期无法完成的!这能在编译期完成
- 代码的复用:
- 利用模板,完成编译期多态,也就是静态多态
- eg:虽然 C++ 的标准容器没有对象继承关系,但彼此之间有着很多的同构性。这些同构性很难用继承体系来表达,也完全不必要用继承来表达。C++ 的模板,已经足够表达这些鸭子类型。
函数模板:杂交出来的!或者说是分工合作
- 编译期会处理一部分,运行期也会处理一部分
- 编译期主要处理类型,运行期主要处理计算
- 不杂交不行:因为运行期无法处理类型
函数与模板的合作方式:
- 函数模板
- 编译期会处理一部分,运行期也会处理一部分
- 编译期主要处理类型,运行期主要处理计算
- 不杂交不行:因为运行期无法处理类型
- 函数对象、lambda表达式 与 一些C++自带模板进行组合使用
函数与struct模板的区别
- 执行期不同:
- 函数在运行期执行
- 模板在编译器运行
- 处理对象不同:
- 函数的处理对象:也就是函数的输入,只能是数据,包括以包含数据的容器、普通类型遍历
- 模板的处理对象:类型(容器类型,自定义类型) + static const 常量。不是数据
- body内容不同:
- 函数体内:定义的是数据运算规则
- 函数的编译过程会报错
- struct模板体内:定义的是类型推导规则
- 模板的编译过程由于有SFINAE不会报错
- 函数体内:定义的是数据运算规则
- 将编译过程分为两个步骤:先模板编译,后函数编译
泛型的好处:
- 可以统一数据类型,便于操作。
- 将运行时的异常提前到了编译时,提高了效率。
- 避免了强制类型转换
- 实现代码的模板化,把数据类型当作参数传递,提高了可重用性。
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