这里是《C++ Templates》第二版的读书笔记
变参模板(Variadic Template)
使用方式如下:
|
|
这里的Args
被称为template parameter pack
,而args
被称为function parameter pack
。
这里有一个输出每个参数的例子:
|
|
使用模板变参的时候不能够像使用vector一样去遍历,我们只能通过递归的方式处理参数。
这里Print
调用之后,所有的参数都会被传入void Print(T, Args...)
中,这会让此函数展开为void Print<int, int, const char*>(1, 2, "string")
,而1会传给value
,剩下的参数会传给args
,所以会输出1。然后此函数再次递归地调用自己,将2输出,再是最后的字符串,直到没有任何参数剩下时,会调用普通的print(){}
函数结束递归。
如果存在普通参数模板(如template <typename T> void Print(T value)
),则会在只剩下最后一个参数时调用此模板函数(因为此模板的条件最为精确)。
sizeof...
在C++11中,可以通过sizeof ...
操作符得到模板变参的个数:
|
|
或者写sizeof...(Args)
也可以。
折叠表达式(Fold Expression)
C++17开始可以使用折叠表达式来对模板变参进行一些简单操作:
|
|
这个式子对所有的参数进行求和。
有如下可能的折叠表达式形式:
折叠表达式 | 如何求值 |
---|---|
(... op pack) | (((pack1 op pack2) op pack3 ... op packN) |
(pack op ...) | (pack1 op (pack2 op (... (packN-1 op packN)))) |
(init op ... op pack) | (((init op pack1) op pack2) ... op packN) |
(pack op ... op init) | (pack1 op (pack2 op (... (packN op init)))) |
看完这个你应该知道为什么他叫Fold Expression了,没错,这就是函数式编程中的“折叠”(Fold)。而且还分为左右折叠。
实际应用
变参模板用的最多的可能就是将函数参数进行转发了,比如我们可以改良上一篇文章中的内存池,让他通过不同的构造函数构造对象:
|
|
一般都会用Args&&... args
进行完美转发,这个后面再说。
变参表达式
指一些好用的表达式:
|
|
这个函数会将所有的参数翻倍,即下面两行是等价的:
|
|
甚至可以和数字相加:
|
|
这会将所有的参数+1。
变参下标
看看这种用法:
|
|
下面是一种展开例子:
|
|
也可以用非类型模板参数:
|
|
变参模板类
模板类也可以使用变参,这种方式在C++标准库中屡见不鲜,比如tuple
的声明:
|
|
Variant
也使用了这种声明。
或者我们可以发挥想象:变参不仅仅可以指定成员变量的类型,它还可以指定基类的类型:
|
|
这样就创造了一个集成多个类的模板类。