1 问题
问:如下代码中,调用func1的时候,只能递归一次取到一个值,有没有什么方法像func2中那样,一次把所有的值都拿出来呢?
2 回答
先了解与此相关的C++三个基本知识点:
- 二元操作 (binary operator) 」 :需要两个操作数的操作,比如加法操作: a+b,再如输出流操作 : cout << ‘a’ ;二者分别对应 + 操作符和 << 操作符。
- 参数包 (parameter pack) 」: “正经”的函数模板的入参数是一个 伪类型,再加一个伪形参名,比如: T1 t1 。 参数包 则在二者中间加一个小三…,比如 T1 … t1。
- 然后,C++17 的“二元操作”,支持其中一个使用 “参数包/pack”。编译器遇到二者,就会自动从“包”中取出一个参数,作为眼下就要进行的二元操作的一个操作数,进行操作。取参数次序还可以区分正序(从左到右排列)或倒序(从右到左排列)。
2.1 你要的“一次性取出”的例子
template <typename T1, typename ...T2s> // #1
void func1(T1 a, T2s ... rest) // #2
{
std::cout << a; // #3
(std::cout << ... << rest) << "\n"; // #4 rest:余下的
}
2.2 例子代码解读
- #1:typename 和 T1 之间干干净净,而 typename 和 T2s 之间夹着小三点,所以T1是一个正经的模板参数,但T2s是一个模板参数包;它可能是0个或1个或更多个参数类型;
- #2:T1和a之间干干净净,而T2 和 rest 之间夹着 小三点,所以 a 是一个参数,而 … 和 rest 在此共同表示 一个包(参数包)。
- #3:先是 std::cout 正经地输出了正经的入参 a;
- #4:然后遇上参数包, “(cout << … << rest)”,理解为 << (包中第1个参数);即首先从“包”中取眼下的第1个参数(如果有)作为 操作数2 ,与固定的操作数1(就是cout),进行 “<<” 运算——其实就是“输出”—— 然后,再取包中剩下的,如此反复,直至把参数包掏空。
- 继续上一点:这里 “…” 和 具体的参数名(本例中的 rest )在语法与语义表达上有了分工:“…”表示包中的当前第一个,而 具名参数 rest 表示:“请继续如此处理包中余下的参数……”。另外 ,外围的括号( ) 是必须的,表示在 #4 这行完整的语句中,参与和参数包有关的求值的准确范围(一个表达式);所以 << “\n” 只会在参数包处理完成后执行只执行一次。事实上,哪怕把 << “\n” 从代码中删除,括号也得留着。
注:小括号(或称圆括号)在C++中,并不仅仅用于改变优先级,并且在优先级不改变的情况下,有时候它也不能省略。更非可有可无。括号可强制令相关代码成为一个表达式。这点和 一元的 + (俗称正号)在某些地方,有和没有完全不同(有+号,会让修饰的代码变成表达式,典型如将一个 lambda 变成一个 表达式,最终转换成 函数指针的做法,以后有空再说)。
2.3 调用示例
main()
{
func1(12, " + ", 13, " == 25");
}
将输出: 12 + 13 == 25。
空包调用示例:
func1("Hello world!"); // 此时,形参 ... rest 是个空包
2.4 支持的“原生”的二元操作符
共32个:
+ - * / % ^ &
| = < > << >>
+= -= *= /= %= ^= &= |= <<= >>= == != <= >=
&& || , .* ->*
趁热练练手?请往下滚动屏幕……