零、问题
如下代码中,调用func1的时候,只能递归一次取到一个值,有没有什么方法像func2中那样,一次把所有的值都拿出来呢?
回答
二元操作 (binary operator) 」 :需要两个操作数的操作,比如加法操作: a+b,再如输出流操作 : cout << 'a' ;二者分别对应 + 操作符和 << 操作符。
参数包 (parameter pack) 」: “正经”的函数模板的入参数是一个 伪类型,再加一个伪形参名,比如: T1 t1 。 参数包 则在二者中间加一个小三...,比如 T1 ... t1。
然后,C++17 的“二元操作”,支持其中一个使用 “参数包/pack”。编译器遇到二者,就会自动从“包”中取出一个参数,作为眼下就要进行的二元操作的一个操作数,进行操作。取参数次序还可以区分正序(从左到右排列)或倒序(从右到左排列)。
结合你要的例子:
template <typename T1, typename ...T2s> // #1
void func1(T1 a, T2s ... rest) // #2
{
std::cout << a; // #3
(std::cout << ... << rest) << "\n"; // #4 rest:余下的
}
解读(示例方法,学会就能轻松阅读带 ... 的模板参数):
- #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 变成一个 表达式,最终转换成 函数指针的做法,以后有空再说)。
调用:
int main()
{
func1(12, " + ", 13, " == 25");
}
将输出: 12 + 13 == 25
或者给个空包:
func1("Hello world!"); // 形参 ... rest 是个空包
最后,支持的“原生”的二元操作符,共32个:
+ - * / % ^ & | = < > << >>
+= -= *= /= %= ^= &= |= <<= >>= == != <= >=
&& || , .* ->*
作业:
来来来,写一个 my_sum( ) ,求一个多个数(整数或非整数)的累加和;要求不丢精。
/* 答题纸 */
答案:
template <typename T1, typename ... Rest>
auto my_sum(T1 n1, Rest ... rest)
{
return (n1 + ... + rest);
}
int main()
{
std::cout << my_sum(1, 2, 3.3, 4, 5);
}