在C++0x之前,模板(包括类模板和函数模板)参数的个数和类型是固定不变的,而且都必须在定义时预先确定。在C++0x中,这一点得到了改善,模板(无论类模板还是函数模板)将具有指定任意个数任意类型参数的能力,这就是所谓的变长参数模板(Variadic Templates)。
下面我们就用代码说明带变长参数的函数模板。
下面我们就用代码说明带变长参数的函数模板。
C++代码
- #include <iostream>
- using namespace std;
- template<typename T>
- void output(T&& value)
- {
- cout << value << endl;
- }
- template<typename First, typename... Rest>
- void output(First&& first, Rest&&... rest)
- {
- cout << first << ",";
- output(forward<Rest>(rest)...);
- }
- template<typename T>
- T sum(T&& value)
- {
- return value;
- }
- template<typename First, typename Second, typename... Rest>
- First sum(First&& first, Second&& second, Rest&&... rest)
- {
- return sum(first + second, forward<Rest>(rest)...);
- }
- int main()
- {
- double d = 2.3;
- output(42,"hello",d,'a');
- output("hello",d,'a');
- output(d,'a');
- output('a');
- output(sum(1), sum(1, 2), sum(1, 2, 3));
- return 0;
- }
- //42,hello,2.3,a
- //hello,2.3,a
- //2.3,a
- //a
- //1,3,6
代码说明
- 代码中的 output 与 sum 是两个可接受变长参数的函数模板。
output 函数接受一个及以上的参数,函数功能为输出各个参数的值。
sum 函数同样接受一个及以上的参数,函数功能为计算各个参数的和。
- 代码中output 函数有两个版本。
- 参数只有一个时调用4~8行所定义的非递归版本,
此版本参数个数固定,C++11之前即已合法。 - 参数有两个及以上时调用9~14行所定义的递归版本,
此版本参数个数可变,依赖C++11新增的语言特性。 - 代码第9行模板参数列表中的 typename... Rest 部分被称作模板参数包
实际调用时模板参数包 Rest 内将存放可变长部分各参数(第1个参数first之后的所有参数)的类型
如第30行用 output(42,"hello",d,'a'); 调用时,Rest 的内容为(char*, double&, char) - 代码第10行函数参数列表中的 Rest&&... rest 部分被称作函数参数包
实际调用时函数参数包 rest 内将存放可变长部分各参数(第1个参数first之后的所有参数)的值
如第30行用 output(42,"hello",d,'a'); 调用时,rest 的内容为("hello",d,'a') - 代码第13行中的...被称为解包运算符,
实际调用时解包运算符将把它之前的表达式(即forward<Rest>(rest))作为模板来展开
模板参数包Rest 以及函数参数包rest,展开之后所生成的各表达式将用逗号连接,
如第30行用 output(42,"hello",d,'a'); 调用时,forward<Rest>(rest)... 将被展开为
forward<char*>("hello"), forward<double&>(d), forward<char>('a') - 由以上分析可知
递归版本的output函数输出第一个参数first的值以及逗号之后用剩余参数递归调用自身,
非递归版本的output函数则只输出唯一一个参数value的值,不会输出逗号。 - 第30行用 output(42,"hello",d,'a'); 调用时模拟运行结果如下
output(42,"hello",d,'a')
= 输出"42,“ 之后调用 output(forward<char*>("hello"), forward<double&>(d), forward<char>('a'))
= 输出"42,“ 之后调用 output("hello", d, 'a')
= 输出"42,hello,“ 之后调用 output(forward<double&>(d), forward<char>('a'))
= 输出"42,hello,“ 之后调用 output(d, 'a')
= 输出"42,hello,2.3,“ 之后调用 output(forward<char>('a'))
= 输出"42,hello,2.3,“ 之后调用 output('a')
= 输出"42,hello,2.3,a“
注意最后一步调用非递归版本,因而没有输出逗号。 - 与output 函数相类似,sum 函数同样有两个版本
重复以上分析可知
递归版本的sum 函数计算前两个参数first及second的和之后使用计算结果与剩余参数递归调用自身,
非递归版本的sum 函数则返回唯一一个参数value的值。
- 第34行用 sum(1,2,3); 调用时模拟运行结果如下
sum(1,2,3)
= sum(1+2, forward<int>(3))
= sum(1+2, 3)
= sum(1+2+3)
= 1+2+3
= 6