模板参数遍历 | C & C++
本文介绍了使用 C++ 模板进行参数遍历的两种方式。
普通写法
1 | void expand() {} |
这种写法比较容易理解:通过递归的方式展开所有参数。当函数参数不为空时,会匹配到第二个参数,当函数参数为空时,会匹配到第一个参数。
封装写法
下面看一种抽象得比较好的写法:
1 | template <size_t I = 0, typename FuncT, typename... Tp> |
这种写法把对每个参数的处理单独抽象成一个参数,只需传入一个函数作为参数,这个函数就是要对每个参数进行的处理。
这种写法看起来有点复杂,其实原理和前面那种是一样的,也是通过递归的方式对所有参数依次处理。这里涉及到了一个编译期函数 std::enable_if_t
,先来解释下这个函数的作用,还是先看示例:
1 | // 如果 T 的类型是 int,则定义函数 int read(void* = nullptr);否则不定义该函数 |
可以看到如果函数 std::enable_if_t
的模板参数只有一个,则作为定义该函数与否的判断;如果模板参数有两个,则成功匹配后整个函数等价于第二个模板参数。
了解了函数 std::enable_if_t
的用法后,再分析上述模板参数遍历的实现原理:
- 把可变模板参数转成一个 tuple,对该 tuple 进行递归
- 可以看出两个函数中的
std::enable_if_t
不会同时成立,因为sizeof...(Tp)
的值是固定的 - 在第一次匹配时,如果 tuple 的大小不为 0,则第二个函数的
std::enable_if_t
的模板参数为 true,即匹配到第二个函数,取到模板参数的第一个放到函数f
中执行 - 在整个匹配过程中,
I
的取值为 0、1、2、…、n(n = sizeof…(Tp)),函数的匹配情况会根据std::enable_if_t
的模板参数是否为 true 而来,即I
小于 n 时,匹配第二个函数,I
等于 n 时,匹配第一个函数(作为递归的终点),最终完成对所有函数的处理
参考
模板参数遍历 | C & C++