模板与泛型编程笔记(一)入门篇

1. 推荐书籍

《C++新经典 模板与泛型编程》难得的很容易看得懂的好书,作者讲技术不跳跃,娓娓道来,只要花点时间就能看懂。

2. 笔记

2.1 模板基础
  • 模板为什么要用尖括号?因为便于编译器解析,可以将模板和普通函数声明分开。其实尖括号就相当于函数声明中,用于把参数包裹起来的圆括号,其中参数,就是传入的类型模板参数。如
template <typename T> // 尖括号相当于函数的圆括号,T相当于函数的形参
class A {}

当然,模板支持传递的参数种类不止一种,有类型模板参数,非类型模板参数,模板模板参数。其实对于没有学习过模板的同学来说,这几种类型,即使说了也看不懂。就像某些教材一样,只负责陈述,不负责让你看懂。上面说的内容,在书中的前几章,有很多例子,一看就懂。

综上,可以认为模板就是一个编译期的代码生成函数,这个函数最原始的用途是在编译器解析后,生成实例化后的代码。后面发现,模板竟然支持编译期的分支判断和循环控制,支持对代码生成过程,增加更加精细和复杂的控制。逐步发展成了元编程,简单来说就是编译期编程。

2.2 模板特化
  • 一直搞不懂偏特化的模板怎么写,怎么推导。直到看到了《C++新经典 模板与泛型编程》的第5.1.3节,如以下
// 泛化版本 先写好泛化模板
template <typename T>
class A {}

// 偏特化版本
template <typename T> // 最后写这里,声明一个泛化类型参数
class A<T *> // 因为泛化模板需要一个类型参数,特化版本需要调用泛化版本。所以这里需要传入一个类型,这里假设是T *,T是一个模板参数,需要在使用模板类A的时候指定。
{}

模板的特化版本,必须调用泛化版本。而且特化版本不能单独存在,必须先写出泛化版本的模板。综上可以理解为,特化版本是对泛化版本的一次编译期的“函数”封装,因为有一个调用的过程,所以这里我称之为函数封装。正因为是函数封装,在调用泛化模板时,需要保持参数数量相同。综上,模板写起来有很多限制,语法不是那么随意,这样的好处是便于编译器解析。

2.3 总体把握模板技术
  • 整个C++模板与泛型系统的底层逻辑是:调用模板,通过实参确定类型,根据特化情况,调用匹配的模板函数,推导和展开其他类型,直到无法继续展开。其中,类型匹配机制,实现了编译期的分支控制;递归调用模板,实现了循环控制,两种机制统称为编译器类型推导。二者结合,几乎所有的逻辑都可以实现。

  • 常见的模板类型有:函数模板,类模板,变量模板,别名模板。

  • 只有函数模板在使用的时候,可以不指定模板参数,编译器会自动推导,只需要指定函数实参即可。这就是为什么有时候模板不需要传一个用尖括号包起来的类型参数的原因。其他模板在使用的时候需要指定模板参数,也就是类型模板参数。

  • 模板的优点是编译期执行一些编译期间能够确定的逻辑,提高执行效率,省去一些动态类型转换与判断造成的性能损耗。如果一些信息不能在编译期确定,只能在运行期间确定,那这种情况下模板就发挥不了作用。模板还可以提高代码复用性,总体来说,模板是和代码架构相关的技术,一般需要花精力好好设计的可复用模块,才可能需要使用模板。任何技术都有它的应用场景,实事求是,因地制宜,才是少走弯路的方法。

  • 编写模板要大胆的写,编译器能解析的模板,就是正确的模板。一般来说,只要是能推导的,都能编译通过。模板的语法,可以在尝试中探索和掌握。

  • 总体来说,编译期的泛型编程和C/C++逻辑基本上是一致的,只是泛型编程的核心是类型,类型就是泛型编程中的变量。一定要掌握核心逻辑才能学的轻松。

2.4 万能引用
  • 万能引用,只有两种使用场景,一是函数模板与完美转发,二是 auto &&。
    目前C++中一共有三种引用:左值引用,右值引用,万能引用。一句话理解万能引用:只要你用了万能引用,编译器就会向上找这个引用最原始的来源,从而确定,这个万能引用,到底是左值引用来使用,还是作为右值引用来使用。专业术语,叫引用折叠,不理解的话是真的不好懂。
2.5 萃取
  • 萃取,就是由类型匹配机制,推导出另外一种类型,或者推导出一个值,前者叫类型萃取,后者叫值萃取。当然要看书中的例子,不然这句话肯定是无法理解的。
  • 萃取,实际上就是分支判断。利用萃取中的SFINAE,就可以编写很多编译期的判断函数模板、判断类模板,一起组成编译期函数库,用来实现更加复杂的模板逻辑。
2.6 高大上词汇
  • 显式具体化:就是全特化,因为全特化后的模板,不需要推导,它就是一个实实在在可以被编译到cpp中的函数,或类,或变量。它的声明需要被写到头文件中,实现需要被写到cpp中,如果写在头文件中可能会出现重定义错误。

  • 显式实例化:就是给某个模板传递好类型参数,一旦类型确定,模板就被实例化成可以被编译到cpp中的函数,或类,或变量。显示具体化和显式实例化本质上是一样的,显式具体化是通过全特化写法实现的;显式实例化,是通过传递具体的类型参数,将模板实例化得到的。显式实例化只需要写到cpp中。

  • 隐式实例化:在模板使用的时候,传递确定的类型参数,这个时候,编译器会自动生成代码,不需要开发者参与,所以叫隐式实例化。

2.7 元编程

类模板,函数模板等都是元编程。

需要特别注意的元函数是,通过递归的方式生成内联代码的元函数。

通过递归生成代码机制,可以实现变长的类型列表(typelist),变长的多类型容器(tuple)。这个太绕,暂时知道即可,需要用的时候再仔细研究吧。

2.8 标准模板库的实现原理

一般开发中我们是学者怎么用标准模板库写业务代码,而比较少能看懂标准模板库的代码,书中这一块有具体讲解,可以当作闲暇读物。


入门篇完结。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

撬动未来的支点

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值