GP and Templates(泛型编程与模板) 现实中许多问题错综而复杂,解决起来极为不易。 人们发现可以将这些问题拆解成更小的问题来进行解决。往往当把问题拆解到最小模块的时候,便能对问题产生新的理解。 能解决问题的根本在于:问题整体可分解为部分,部分可组成为整体,整体等于部分之和。这是还原论的思想,在编程世界叫作分治策略,实现手法分为迭代和递归。 向下拆分,向上总结,就能把一个大问题化为许多小问题,通过解决一个个小问题,最终就能解决整个大问题。 解决之时,需要描述问题的数据和关系。 在C++中,用面向对象来表示事物的整体结构,用数据结构来表示数据之间的关系,用算法来表示具体的逻辑实现。 数据有类型之分,而不同的问题可能本质上实为同类问题,这便导致每次遇到同类问题,还得提供不同类型版本的解法。 因此数据和方法应该进行分离,对数据进行更高层次的抽象。 面向对象编程的目的是将数据和方法关联起来,而为了分离数据和方法之间的依赖,出现了泛型编程。 泛型编程本质上就是对数据类型的高度抽象,使同一个方法能作用到不同的类型,为同类问题提供了一个通解。 C++泛型编程所提供的支持组件便是模板,而Variadic Templates(可变参数模板/可变模板参数),顾名思义,就是支持任意个数、任意类型的模板。 对于一类问题,若发现变化的永远是输入,而解决方法始终不变,便可以使用可变参数模板定义通解。 这个通解,就是问题的「最小模块」,通过迭代或递归,分多次解决分解后的小问题,最终就能解决整个问题。 然而迭代需要持有一个迭代器并改变它,直到某个条件符合。泛型编程发生于编译期,编译期中的整数计算(如enum)或是typedef(using)的类型定义之后就无法进行改变。所以迭代虽然比递归表示起来更加自然,却无法进行实现。 递归符合一切编译期编程的要求,所以可变参数模板由其实现。 由于递归本身的特性,再结合可变参数模板,使得理解起来极为不易。因为你无法看到实际的结果,对于无法看到的东西,理解起来总是比较困难的。 Ellipsis operator and Parameter pack(省略操作符与参数包) 先来看一个简单的可变参数模板示例: template <class… Args> void func(Args… args) { std::cout << sizeof…(args) << std::endl; } … 叫做省略操作符(ellipsis operator),用以表示任意个参数,带省略号的参数称为参数包(Parameter pack)。 当省略操作符出现在参数名(args)左边时,标示着参数是一个参数包;出现在参数名右边时,用于扩展参数包。因为我们无法直接获取参数包 args 中的每个参数,所以只能通过展开的方式来获取,那么如何展开参数包就成了难点所在。 现在 func 函数可以接受任意个参数: func<>… Continue Reading Understanding variadic templates

上篇介绍了 C++20 协程的诸多内容,独余 co_await 未曾涉及,它是协程中非常重要的一个关键字,用以支持挂起(suspend)和恢复(resume)的逻辑。 本篇便专门来对其进行介绍。 Awaitable type and Awaiter type 较于普通函数,协程支持挂起和恢复。那么何时挂起,何时恢复,便是逻辑之所在。 由于许多问题在解决时都无法立刻得到答案,即结果存在延迟性,在程序中就表现为阻塞。阻塞会导致CPU大量空闲,效率大减,于是就要想办法实现非阻塞。 多线程便是解决阻塞的一个方式,遇到阻塞,便由操作系统进行切换调度,以此实现非阻塞。重叠 IO 亦是一个非阻塞方案,遇到阻塞,提供一个回调函数给操作系统,系统在阻塞完成后调用其继续执行。 这些方案,本质上都是在处理如何挂起和恢复的问题。换言之,就是在遇到阻塞时暂停当前工作,先去进行别的工作,等阻塞完成后再回来继续完成当前工作。 既然拥有共同的处理问题的逻辑,那么对其进行抽象,便能得到一个高层级的类型。这个高层级的类型便是 Awaitable type(即上篇的 Awaitable object)。 简单地说,Awaitable type 就是对阻塞问题进行总结、归纳、提炼要点,所得到的模型。 那么有何好处呢? 好处就是,我们只要依照抽象后所得模型中的一些规则,便能定义出所有类似问题的解决逻辑,所有类似问题都能依此模型进行解决。 乍听很复杂,其实并不难,只要依规则行事便可。 那么具体规则又是什么? 其实只是三个接口: await_ready() await_suspend() await_resume() 它们分别代表着:是否阻塞、挂起、恢复。 将程序中阻塞完成的条件,写到 await_ready 函数中,便能依此决策何时挂起,何时恢复。 一个类型若直接实现了这三个接口,那么这个类就被称为 Awaiter。 什么意思呢?若类型 A 本身并未实现这三个接口,而是通过类型 B 实现的,那么类型 A 就称作 Awaitable,类型 B 称作 Awaiter。… Continue Reading C++20 Coroutines: operator co_await