参数包两种解开方式
递归展开参数包
展开参数包的思路如下
- 我们给函数模板增加一个参数T 它的作用是分离出一个参数出来
- 在函数模板中递归调用该函数模板 调用时传入剩下的参数包
- 依次递归 直到所有的参数都被分离一次
代码表示如下
template<class T,class ...Args> void ShowList(T value,Args... args) { cout <<typeid(value).name() << endl; ShowList(args...); }
但是这里我们会面临一个问题 这个问题是假如我们递归到了最后的无参
就没有ShowList函数能够匹配这个实例了
所以说我们必须还要再加一个无参版本的ShowList函数
void ShowList() { cout <<endl; }
这样一来递归到最后的无参版本就会终止递归
这里还有两个注意点
- 如果外部调用ShowList函数时就没有传入参数 那么就会直接匹配到无参的递归终止函数
- 我们本意是想让外部调用ShowList函数时匹配的都是函数模板 并不是让外部调用时直接匹配到这个递归终止函数
所以说我们可以针对代码做以下改进
void ShowListArgs() { cout << endl; } template<class T,class ...Args> void ShowListArgs(T value,Args... args) { cout <<typeid(value).name() << endl; ShowListArgs(args...); } template<class ...Args> void ShowList(Args ...args) { ShowListArgs(args...); }
我们可以将展开函数和递归调用函数的函数名改为ShowListArgs
并且重新写一个模板函数 使用函数包作为参数
这样子不管传入多少的参数匹配的函数就都是ShowList了
演示结果如下
逗号表达式展开参数包
我们都知道 数组可以使用列表初始化
比如说像这样
int a[] = {1,2,3,4}
除此之外 如果参数包中各个参数的类型都是整型 那么也可以把这个参数包放到列表当中初始化这个整型数组 此时参数包中参数就放到数组中了
代码表示如下
template<class ...Args> void ShowList(Args ...args) { int arr[] = { args... }; // 这里展开args省略号要在后面 for (auto e : arr) { cout << typeid(e).name() << " "; } cout << endl; }
此时我们就可以像ShowList中传入多参数了 代码和演示结果如下
ShowList(1); ShowList(1,2,3); ShowList(1,2,3,4);
但是C++规定 一个容器中只能容纳一种类型的数据 所以当我们传入多种类型的参数就会发生下面的情况
这个时候提出的一个解决方案就是逗号表达式
逗号表达式 运行完所有的参数之后返回最后一个值
这样子只要我们设置最后一个值为一个整型 然后再创建一个新的函数来接受参数包的每个数据就好了
代码表示如下
// 模板函数打印出参数包中每个参数的类型 template<class T> void Print(T& t) { cout << typeid(t).name() << " "; } template<class ...Args> void ShowList(Args ...args) { // 这里展开args省略号要在后面 int arr[] = { (Print(args),0)... }; cout << endl; }
试验数据和演示结果如下
ShowList(1,string("hello")); ShowList(1, 1.1); ShowList(1, 2, 3, 4);
但是此时仍然存在者一个问题 那就是在C++中 这段代码是会报错的
int arr[] = {};
C++语法规定 我们不能分配常量大小为0的数组
这个问题反应到我们现在写的代码上就是我们无法在无参的情况下调用函数
解决这个问题的方式也很简单 我们写出一个特化版本的函数就可以
void ShowList() { cout << endl; }
还有就是关于省略逗号表达式的问题
逗号表达式存在的意义只是为了让容器中的类型统一 那么只要类型原本就是统一的 我们就能够省略逗号表达式
具体的思路如下
我们让Print函数返回一个int类型的值 之后在列表中展开即可
代码表示如下
template<class T> int Print(T& t) { cout << typeid(t).name() << " "; return 0; } void ShowList() { cout << endl; } template<class ...Args> void ShowList(Args ...args) { // 这里展开args省略号要在后面 int arr[] = { Print(args)... }; cout << endl; }
我们还是用之前相同的测试样例 测试后结果如下