C++11 -- 可变参数模板

news/2024/10/18 5:37:47/

文章目录

  • 可变参数模板的概念
  • 可变模板参数的定义
  • 获取可变模板参数包的值
    • 递归函数方式展开参数包获取
    • 逗号表达式展开参数包获取
  • STL容器中的emplace相关接口参数
    • emplace_back与STL容器中的push_back的主要区别
    • emplace_back与push_back的差异原理
    • emplate_back与push_back的区别验证

可变参数模板的概念

( 1 ): C++98/01中,类模板和函数模板只能包含固定数量的模板参数.

( 2 ): 相比于C++98/01,C++11的新特性可变模板参数模板能够让我们创建可以接受可变参数的函数模板和类模板.

可变模板参数的定义

以下就是一个基本可变参数的函数模板.

//Args是一个模板参数包,args是一个函数形参参数包.
//声明一个函数形参参数包Args...args,其中这个形参参数包中可以包含0到任意个模板参数.
template < class ...Args >
void ShouList( Args ...args )
{}

注意:
上面的参数args前面有省略号,所以它就是一个可变模板参数,我们将带省略号的参数称为"参数包",它里面包含了0到N个(N>>0)个模板参数.

此外,我们也可以在函数模板内中使用sizeof计算函数形参参数包中参数的个数.

template <class ...Args >
void ShouwList( Args... args)
{cout << sizeof...( args ) << endl;
}
int main()
{string str("zh");ShouwList('A', str);           //2ShouwList(1, 'A', str);        //1}

但是,C++11语法并不支持args[i]这样方式获取可变参数.

//编译无法通过.
template<class ...Args>
void ShowList(Args... args)
{for (int i = 0; i < sizeof...(args); i++){cout << args[i] << " "; //使用args[i]打印参数包中的每个参数}cout << endl;
}

获取可变模板参数包的值

由于我们无法直接获取函数形参参数包args中的每个参数,只能通过展开参数包的方式获取参数包中的每一个参数,这是可变模板参数的一个主要特点,也是最大的难点.但是,我们可以通过一些特殊的办法来获取函数形参参数包的值.

递归函数方式展开参数包获取

( 1 ) :我们的本意是想通过函数递归的方式,将函数参数包每次调用ShouwList时都分为参数类型T,和参数包(个数-1)的形式,并将此时的函数参数类型T的参数值t打印出来.

( 2 ): 当函数参数包的参数个数为0时,此时与原来的ShouwList函数中的两个形参并不匹配,这时便不能继续递归了.此时,类似与我们以前写递归的结束条件一样,我们需要额外再写一个递归终止函数,随后编译器就会递归调用该函数.

//递归终止函数.
void ShouwList ( )
{cout << t << endl;
}//利用递归将函数形参参数包展开.
template <class T,class  ...Args >
void ShouwList( T value,Args... args  )
{cout << value << " ";ShouwList(args...);
}
int main()
{ ShouwList(1);                    //1 ShouwList(1, 'A');              // 1, AShouwList(1, 'A', std::string("sort")); // 1, A,sort
}

逗号表达式展开参数包获取

这种展开参数包的方式,不需要通过递归终止函数,而是在expand函数体中展开的.

  • 其中PrintArg是一个处理参数包中每一个参数的函数,每一次处理即对每一种参数t的打印.
  • 我们知道,逗号表达式中会按照顺序执行逗号前面的表达式.并且,expand函数中的逗号表达式:(PrintArg(args),0)也是按照这个顺序执行,先执行PrintArg(args),然后,再得到该一次逗号表达式的结果为0.
  • 同时,该处运用到了C++11的另外一个特性–初始化列表,即通过初始化列表来初始化一个变长的数组,{( PrintArg(args),0)…}将会展开成((PrintArg(args1),0),(PrintArg(args2),0),( PrintArg(args3),0),etc…),这代表每次展开一个逗号表达式args中参数包的个数就减少一个,最终,会创建一个元素值都为0的数组arr[sizeof…(args)].
  • 于是,在使用初始化列表和逗号表达式构造数组的时候,已经默认将args参数包中的参数都展开了.
//将每次的函数形参进行打印.
template < class T > 
void PrintArg(T t)
{cout << t << " ";
}
//利用递归将函数形参参数包展开.
template <class ...Args >
void ShouwList(Args... args)
{int arr[] = { (PrintArg(args),0)... };cout << endl;
}
int main()
{ ShouwList(1);                    //1 ShouwList(1, 'A');              // 1, AShouwList(1, 'A', std::string("sort")); // 1, A,sortreturn 0;
}

小结:
可变模板参数一般用于需要传递多个形参的函数.

STL容器中的emplace相关接口参数

emplace_back与STL容器中的push_back的主要区别

如果插入实参为int,char等内置类型,那么push_back与emplace_back没有区别.
在这里插入图片描述

可是,如果我们存储的数据类型为自定义类型,例如:pair,区别如下.

  • 使用push_back插入数据时,只允许传递一个实参.
  • 使用emplace_back插入数据时,可以允许传递多个实参.
#include <vector>
int main()
{ vector<pair< std::string,int>> v1;v1.push_back(make_pair("sort", 1));v1.emplace_back("sort", 1);return 0;
}

emplace_back与push_back的差异原理

  • emplace_back指:当需要传递多个实参时,emplace_back利用将参数包展开的方式提出来,然后直接对vector中pair进行构造.
  • push_back指:先利用传过来的一个实参构造pair,然后再利用构造出来的pair去构造vector中的pair.(如果构造出来的pair为左值,那就是拷贝构造,如果构造的为右值,那就是移动构,具体看vector中的具体实现).
    在这里插入图片描述

emplate_back与push_back的区别验证

在vector中,使用emplace_back和push_back传递多个实参调用时没有区别,都是调用一次构造加一次拷贝构造,这种为特殊情况,具体要看vector中的emplace_back的源码.
在这里插入图片描述

但是对于STL中其他容器来说,例如:list.在相同情况下,emplace_black相比于push_black来说少调用了一次拷贝构造.
在这里插入图片描述

总结:
综合以上情况,我们在插入数据时尽量使用emplace_back,进而更加高效.


http://www.ppmy.cn/news/86583.html

相关文章

delphi11.3的WebBrower支持Edge

看到此消息后&#xff0c;立即下载了DelphiCE&#xff08;社区版&#xff09; 安装&#xff0c;创建项目&#xff0c;放TWebBrowers&#xff0c;TButton 配置参数为EdgeOnly F9运行&#xff0c;没反应 配置参数为EdgeIfAvailable F9运行&#xff0c;提示浏览器版本过低 全网搜索…

net::err_aborted 错误码 404

net::err_aborted 错误码 404 一般来说&#xff0c;在jsp中引入js、css时就可能会出现net::ERR_ABORTED 404&#xff08;即Not Found&#xff09;错误&#xff0c;即前端报错&#xff0c;解决方式也有两种比较常见的方式。具体介绍如下&#xff1a; 解决方法一 在js&#xf…

《深入理解Java虚拟机》 JAVA 字节码指令 基础

1.操作数栈 解释时&#xff0c;JVM会为方法分配一个栈帧&#xff0c;而栈帧又由 局部变量表&#xff0c;操作数帧&#xff0c;方法引用&#xff0c;动态链接 组成 方法中的每条指令执行时&#xff0c;要求该指令的操作数已经压入栈中&#xff1b;执行指令时会将操作数从栈中弹…

【算法】Letter Tile Possibilities 活字印刷

文章目录 Letter Tile Possibilities 活字印刷问题描述&#xff1a;分析代码回溯动态规划 Letter Tile Possibilities 活字印刷 问题描述&#xff1a; 你有一套活字字模 tiles&#xff0c;其中每个字模上都刻有一个字母 tiles[i]。返回你可以印出的非空字母序列的数目。 每个…

python爬取网页代码-python爬虫爬取网页所有数据详细教程

Python爬虫可通过查找一个或多个域的所有 URL 从 Web 收集数据。Python 有几个流行的网络爬虫库和框架。大家熟知的就是python爬取网页数据&#xff0c;对于没有编程技术的普通人来说&#xff0c;怎么才能快速的爬取网站数据呢&#xff1f;今天给大家分享的这款免费爬虫软件让您…

java面试题(MyBatis)

目录 1.什么是MyBatis&#xff1f; 2.MyBatis存在哪些优缺点&#xff1f; 3.MyBatis中#{}和${}的区别 4.MyBatis 有哪几种 SQL 编写形式 5.MyBatis 怎么实现分页 6.MyBatis 如何防止 SQL 注入 7.MyBatis 延迟加载 8.MyBatis 中的缓存机制有啥用&#xff1f; 9.MyBatis…

【数据结构】--单链表力扣面试题①移除链表元素

题述&#xff1a; 给你一个链表的头结点head和一个整数val,请你删除链表中所有满足Node.val val的节点&#xff0c;并返回新的头结点。 思考&#xff1a; 为什么说要返回新的头结点&#xff0c;因为你删除的可能存在把原来的头结点删除的情况&#xff0c;这时就需要有新的头结…

Spring-IOC是什么

Spring-IOC是什么 Spring-IOC是什么IOC是什么 Spring-IOC是什么 Spring-IOC是Spring框架的核心&#xff0c;是一个容器&#xff0c;它负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。 IOC是什么 控制反转&#xff0c;指的是将对象的控制权交给Spring容器&a…