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

相关文章
|
3月前
|
存储 算法 C++
C++ STL 初探:打开标准模板库的大门
C++ STL 初探:打开标准模板库的大门
136 10
|
5月前
|
编译器 C++
【C++】——初识模板
【C++】——初识模板
【C++】——初识模板
|
6月前
|
程序员 C++
C++模板元编程入门
【7月更文挑战第9天】C++模板元编程是一项强大而复杂的技术,它允许程序员在编译时进行复杂的计算和操作,从而提高了程序的性能和灵活性。然而,模板元编程的复杂性和抽象性也使其难以掌握和应用。通过本文的介绍,希望能够帮助你初步了解C++模板元编程的基本概念和技术要点,为进一步深入学习和应用打下坚实的基础。在实际开发中,合理运用模板元编程技术,可以极大地提升程序的性能和可维护性。
|
2月前
|
安全 编译器 C++
【C++11】可变模板参数详解
本文详细介绍了C++11引入的可变模板参数,这是一种允许模板接受任意数量和类型参数的强大工具。文章从基本概念入手,讲解了可变模板参数的语法、参数包的展开方法,以及如何结合递归调用、折叠表达式等技术实现高效编程。通过具体示例,如打印任意数量参数、类型安全的`printf`替代方案等,展示了其在实际开发中的应用。最后,文章讨论了性能优化策略和常见问题,帮助读者更好地理解和使用这一高级C++特性。
85 4
|
2月前
|
算法 编译器 C++
【C++】模板详细讲解(含反向迭代器)
C++模板是泛型编程的核心,允许编写与类型无关的代码,提高代码复用性和灵活性。模板分为函数模板和类模板,支持隐式和显式实例化,以及特化(全特化和偏特化)。C++标准库广泛使用模板,如容器、迭代器、算法和函数对象等,以支持高效、灵活的编程。反向迭代器通过对正向迭代器的封装,实现了逆序遍历的功能。
39 3
|
2月前
|
编译器 C++
【c++】模板详解(1)
本文介绍了C++中的模板概念,包括函数模板和类模板,强调了模板作为泛型编程基础的重要性。函数模板允许创建类型无关的函数,类模板则能根据不同的类型生成不同的类。文章通过具体示例详细解释了模板的定义、实例化及匹配原则,帮助读者理解模板机制,为学习STL打下基础。
39 0
|
3月前
|
编译器 程序员 C++
【C++打怪之路Lv7】-- 模板初阶
【C++打怪之路Lv7】-- 模板初阶
28 1
|
3月前
|
存储 编译器 C++
【C++篇】引领C++模板初体验:泛型编程的力量与妙用
【C++篇】引领C++模板初体验:泛型编程的力量与妙用
59 9
|
3月前
|
编译器 C语言 C++
C++入门6——模板(泛型编程、函数模板、类模板)
C++入门6——模板(泛型编程、函数模板、类模板)
79 0
C++入门6——模板(泛型编程、函数模板、类模板)
|
3月前
|
算法 编译器 C++
【C++篇】领略模板编程的进阶之美:参数巧思与编译的智慧
【C++篇】领略模板编程的进阶之美:参数巧思与编译的智慧
104 2