千家信息网

C++新标准难点解析之什么是可变模板参数

发表于:2025-11-06 作者:千家信息网编辑
千家信息网最后更新 2025年11月06日,本篇内容介绍了"C++新标准难点解析之什么是可变模板参数"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成
千家信息网最后更新 2025年11月06日C++新标准难点解析之什么是可变模板参数

本篇内容介绍了"C++新标准难点解析之什么是可变模板参数"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

前言

C++的新特性--可变模版参数(variadic templates)是C++新增的最强大的特性之一,它对参数进行了高度泛化,它能表示0到任意个数、任意类型的参数。相比C++98/03,类模版和函数模版中只能含固定数量的模版参数,可变模版参数无疑是一个巨大的改进。然而由于可变模版参数比较抽象,使用起来需要一定的技巧,所以它也是C++中最难理解和掌握的特性之一。虽然掌握可变模版参数有一定难度,但是它却是C++11中最有意思的一个特性。

变模版参数的展开

可变参数模板和普通模板的语义是一样的,只是写法上稍有区别,声明可变参数模板时需要在typename或class后面带上省略号"..."。比如我们常常这样声明一个可变模版参数:template

template  void f(T... args);

省略号的作用:

1.声明一个参数包T... args,这个参数包中可以包含0到任意个模板参数; 2.在模板定义的右边,可以将参数包展开成一个一个独立的参数。

省略号的参数称为"参数包",它里面包含了0到N(N>=0)个模版参数。我们无法直接获取参数包args中的每个参数的,只能通过展开参数包的方式来获取参数包中的每个参数,这是使用可变模版参数的一个主要特点,也是最大的难点,即如何展开可变模版参数。

可变模板参数分类:

1.可变模版参数函数

2.可变模版参数类

打印可变模版参数函数的参数个数

#include  #include  using namespace std; template  void print(Type ...data)  {     cout << sizeof...(data) << endl; } int main()  {     print();     print(1);     print(1, "ILoveyou");     print(1, 2, 3.4, "IMissyou");     return 0; }

上面的例子中,print()没有传入参数,所以参数包为空,输出的size为0,后面两次调用分别传入两个和三个参数,故输出的size分别为2和3。由于可变模版参数的类型和个数是不固定的,所以我们可以传任意类型和个数的参数给函数print。这个例子只是简单的将可变模版参数的个数打印出来,如果我们需要将参数包中的每个参数打印出来的话就需要通过一些方法了。

展开可变模版参数函数的方法一般有两种:

1.通过递归函数来展开参数包。

2.逗号表达式来展开参数包。

递归方式展开参数包

通过递归函数展开参数包,需要提供一个参数包展开的函数和一个递归终止函数,递归终止函数正是用来终止递归的,如下面的例子:

#include  using namespace std; //递归终止函数 void print() {     cout << "递归终止函数" << endl; } //展开函数 template  void print(T data,Type...exData) {     cout << data << endl;     print(exData...); } int main() {     print(1, 2, 3, 4);     return 0; }

上例会输出每一个参数,直到为空时输出"递归终止函数"。展开参数包的函数有两个,一个是递归函数,另外一个是递归终止函数,参数包exData...在展开的过程中递归调用自己,每调用一次参数包中的参数就会少一个,直到所有的参数都展开为止,当没有参数时,则调用非模板函数print终止递归过程。当然上述终止函数也可以写成带参数函数模板:

template  void print(T data) {  cout<

接下来用模板函数作为终止函数写一个不限参求和函数,具体实现代码如下:

#include  using namespace std; //递归终止函数 template  Type sum(Type t)  {     return t; } //展开函数 template  T sum(T a, Type ...b)  {     return a + sum(b...); } int main() {     cout << sum(1, 2, 3, 4) << endl;     cout << sum(1, 2, 3) << endl;     return 0; }

sum在展开参数包的过程中将各个参数相加求和,参数的展开方式和前面的打印参数包的方式是一样的。

逗号表达式展开参数包

递归函数展开参数包是一种标准做法,也比较好理解,但也有一个缺点,就是必须要一个重载的递归终止函数,即必须要有一个同名的终止函数来终止递归,这样可能会感觉稍有不便。有没有一种更简单的方式呢?其实还有一种方法可以不通过递归方式来展开参数包,这种方式需要借助逗号表达式和初始化列表。比如前面打印函数可以改成这样:

#include  using namespace std; //递归终止函数 template  void print(T data)  {     cout << data << "\t"; } template  void print(Type ...exData)  {     int array[] = { (print(exData),0)... }; }  int main() {     print(1, 2, 3);     cout << endl;     print("张三", 1, 3);     return 0; }

这个数组的目的纯粹是为了在数组构造的过程展开参数包。我们可以把上面的例子再进一步改进一下,将函数作为参数,就可以支持lambda表达式了,从而可以少写一个递归终止函数了,具体代码如下:

C++中新特性之:initializer_list详解

C++11提供的新类型,定义在头文件中。

template< class T > class initializer_list;

下面稍微介绍一下initializer_list

一个initializer_list当出现在以下两种情况的被自动构造:

  1. 鸿蒙官方战略合作共建--HarmonyOS技术社区

  2. 当初始化的时候使用的是大括号初始化,被自动构造。包括函数调用时和赋值

  3. 当涉及到for(initializer: list),list被自动构造成initializer_list对象

也就是说initializer_list对象只能用大括号{}初始化。拷贝一个initializer_list对象并不会拷贝里面的元素。其实只是引用而已。而且里面的元素全部都是const的。下面一个例子可以帮助我们更好地理解如何使用initializer_list:

#include  #include  #include   using namespace std;  template  struct S  {     vector v;     S(initializer_list l) : v(l) {         cout << "constructed with a " << l.size() << "-elements lists" << endl;     }     void append(std::initializer_list l) {         v.insert(v.end(), l.begin(), l.end());     }      pair c_arr() const {         return { &v[0], v.size() };     } }; template  void templated_fn(T arg) {     for (auto a : arg)         cout << a << " ";     cout << endl; }  int main()  {     S s = { 1, 2, 3, 4, 5 };                     s.append({ 6, 7 , 8 });             for (auto n : s.v)         cout << ' ' << n;     cout << endl;     for (auto x : { -1, -2, 03 })           cout << x << " ";     cout << endl;     auto al = { 10, 11, 12 };      templated_fn >({ 7, 8, 9 });      templated_fn>({ 3, 5, 7 });             return 0; }

可变模版参数类

std::tuple就是一个可变模板类

template< class... Types > class tuple;

这个可变参数模板类可以携带任意类型任意个数的模板参数:

tuple tp1 = std::make_tuple(1); tuple tp2 = std::make_tuple(1, 2.5); tuple tp3 = std::make_tuple(1, 2.5, "");

可变参数模板的模板参数个数可以为0个,所以下面的定义也是也是合法的:

tuple<> tp;

可变参数模板类的参数包展开的方式和可变参数模板函数的展开方式不同,可变参数模板类的参数包展开需要通过模板特化和继承方式去展开,展开方式比可变参数模板函数要复杂。

模版偏特化和递归方式来展开参数包

基本的可变参数模板类

//前向声明 template struct Sum;  //基本定义 template struct Sum {     enum { value = Sum::value + Sum::value }; };  //递归终止 template struct Sum {     enum { value = sizeof (Last) }; }; int main() {     cout << Sum::value << endl;     return 0; }

继承方式展开参数包

//整型序列的定义 template struct IndexSeq{};  //继承方式,开始展开参数包 template struct MakeIndexes : MakeIndexes {};  // 模板特化,终止展开参数包的条件 template struct MakeIndexes<0, Indexes...> {     typedef IndexSeq type; };  int main() {     using T = MakeIndexes<3>::type;     cout <

可变参数模版消除重复代码

C++11之前如果要写一个泛化的工厂函数,这个工厂函数能接受任意类型的入参,并且参数个数要能满足大部分的应用需求的话,我们不得不定义很多重复的模版定义,比如下面的代码:

#include  using namespace std; template T* Instance(Args... args) {     return new T(args...); } class A { public:     A(int a) :a(a) {}     A(int a, int b) :a(a) {}     A(int a, int b,string c) :a(a) {}     void print()      {         cout << a << endl;     }     int a; }; class B  { public:     B(int a, int b) :a(a), b(b) {}     void print()     {         cout << a << endl;         cout << b << endl;     }     int a;     int b; }; int main() {     A* pa = Instance(1);     B* pb = Instance(1, 2);     pa->print();     pb->print();     pa = Instance(100, 2);     pa->print();     pa = Instance(100, 2,"Loveyo");     pa->print();     return 0; }

万能函数

template  class  MyDelegate { public:     MyDelegate(T* t, R  (T::*f)(Args...) ):m_t(t),m_f(f) {}     R operator()(Args... args)     {         return (m_t->*m_f)(args ...);     }     //R operator()(Args&&... args)      //{             //return (m_t->*m_f)(std::forward(args) ...);     //}  private:     T* m_t;     R  (T::*m_f)(Args...); };     template  MyDelegate CreateDelegate(T* t, R (T::*f)(Args...)) {     return MyDelegate(t, f); }  struct A {     void Fun(int i){cout<

"C++新标准难点解析之什么是可变模板参数"的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!

参数 函数 可变 模板 模版 递归 方式 个数 输出 C++ 类型 面的 例子 特性 过程 代码 表达式 委托 标准 难点 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 软件开发项目进度计划 电信运营商网络安全宣传 无线网络安全性wpa 某学校数据库有三张表 服务器 加网卡 农产品追溯平台软件开发价格 网络安全宣传周宣传活动新闻 电子商务网络技术的心得体会 河北正规网络技术服务创新服务 八年级信息技术网络安全导入 深圳停车系统软件开发报价 南京华艺网络技术有限公司 庆阳陇大互联网络科技有限公司 在家创业做软件开发 违法网络安全第二十七条 淘宝交易修改软件开发 南京质量软件开发优势 湖南工程学院网络安全中心 网络安全国家执行什么政策 如何打开组态王的数据库 上海航恒网络技术 微软登录遇到临时服务器问题 小程序数据库地理查询 互联网项目软件开发 我县开展网络安全宣传周 基本科学指标数据库esi 网络技术学java吗 腾讯云服务器存放数据怎么读取 steam怎么修改服务器地区 宝塔面板默认的数据库访问地址
0