C++11 可变参数模板(下)

简介: C++11 可变参数模板(下)

参数包两种解开方式


递归展开参数包

展开参数包的思路如下

  • 我们给函数模板增加一个参数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了

演示结果如下

472eebc8b9d24bc59ae8da206fc9eb0b.png


逗号表达式展开参数包

我们都知道 数组可以使用列表初始化

比如说像这样

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);

01956b66dfdc445b989e4d48ce26a144.png

但是C++规定 一个容器中只能容纳一种类型的数据 所以当我们传入多种类型的参数就会发生下面的情况

b177d8b89062441ca96804d5e29f229a.png

这个时候提出的一个解决方案就是逗号表达式

逗号表达式 运行完所有的参数之后返回最后一个值

这样子只要我们设置最后一个值为一个整型 然后再创建一个新的函数来接受参数包的每个数据就好了

代码表示如下

// 模板函数打印出参数包中每个参数的类型
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);

55600e0490b0435ebc5f7e2e1a2a98c5.png但是此时仍然存在者一个问题 那就是在C++中 这段代码是会报错的

int arr[] = {};

C++语法规定 我们不能分配常量大小为0的数组

这个问题反应到我们现在写的代码上就是我们无法在无参的情况下调用函数

001afbf252af4fa7a16aafe7d6982e97.png

解决这个问题的方式也很简单 我们写出一个特化版本的函数就可以

void ShowList()
{
  cout << endl;
}

b058db300f92478aa61b26742e3949d9.png

还有就是关于省略逗号表达式的问题

逗号表达式存在的意义只是为了让容器中的类型统一 那么只要类型原本就是统一的 我们就能够省略逗号表达式

具体的思路如下

我们让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;
}


我们还是用之前相同的测试样例 测试后结果如下

8c74399fda674d099d6b6ef327add6d7.png

相关文章
|
5天前
|
编译器 C语言 C++
c++的学习之路:19、模板
c++的学习之路:19、模板
34 0
|
5天前
|
存储 C++ 容器
C++STL(标准模板库)处理学习应用案例
【4月更文挑战第8天】使用C++ STL,通过`std:vector`存储整数数组 `{5, 3, 1, 4, 2}`,然后利用`std::sort`进行排序,输出排序后序列:`std:vector<int> numbers; numbers = {5, 3, 1, 4, 2}; std:sort(numbers.begin(), numbers.end()); for (int number : numbers) { std::cout << number << " "; }`
22 2
|
3天前
|
存储 编译器 C++
|
5天前
|
存储 算法 编译器
C++的模板与泛型编程探秘
C++的模板与泛型编程探秘
11 0
|
5天前
|
编译器 C++
【C++从练气到飞升】08---模板
【C++从练气到飞升】08---模板
|
5天前
|
算法 编译器 C++
【C++入门到精通】新的类功能 | 可变参数模板 C++11 [ C++入门 ]
【C++入门到精通】新的类功能 | 可变参数模板 C++11 [ C++入门 ]
23 1
|
5天前
|
编译器 C语言 C++
【C++】模板进阶
【C++】模板进阶
15 1
|
5天前
|
存储 编译器 C++
【C++】内存管理和模板基础(new、delete、类及函数模板)
【C++】内存管理和模板基础(new、delete、类及函数模板)
25 1
|
5天前
|
C++
【期末不挂科-C++考前速过系列P6】大二C++实验作业-模板(4道代码题)【解析,注释】
【期末不挂科-C++考前速过系列P6】大二C++实验作业-模板(4道代码题)【解析,注释】
【期末不挂科-C++考前速过系列P6】大二C++实验作业-模板(4道代码题)【解析,注释】
|
5天前
|
存储 算法 C++
详解C++中的STL(标准模板库)容器
【4月更文挑战第30天】C++ STL容器包括序列容器(如`vector`、`list`、`deque`、`forward_list`、`array`和`string`)、关联容器(如`set`、`multiset`、`map`和`multimap`)和容器适配器(如`stack`、`queue`和`priority_queue`)。它们为动态数组、链表、栈、队列、集合和映射等数据结构提供了高效实现。选择合适的容器类型可优化性能,满足不同编程需求。