【C++11 ——— 可变参数模板】

news2024/9/21 0:33:20

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

  • 可变参数模板的概念
  • 可变参数模板的定义方式
  • 参数包的展开
    • 递归式展开参数包
    • 逗号表达式展开参数包
  • emplace
    • emplace 的使用
    • emplace 的优势

可变参数模板的概念

C++11之前,函数模板和类模板中的模板参数数量固定的。可变参数模板打破了这个限制,提供了一种编写泛型代码的方法,让我们可以定义接受可变数量参数的模板。这极大地增加了模板的灵活性和表达能力。

可变参数模板的定义方式

函数的可变参数模板定义方式如下:

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

}
  • 模板参数包:template<class ... Args> 中的Args... 表示一个模板的参数包,它可以包含0个或多个模板参数。
  • 函数参数包:void ShowList(Args... args)中的args... 表示一个函数的参数包,允许函数接受任意数量的参数。

...在参数包中的作用


  • ... 在模板参数包中的作用: 用于声明一个可以包含多个参数的包,一般位于参数名称之前。
  • ... 在函数参数包中的作用: 用于表示这个函数可以接受多个参数,一般位于参数名称之后。

当定义了一个可变参数模板之后,就可以像普通函数一样调用它:

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

}

int main()
{
	ShowList(1, 2, 3); // 可以传入任意数量的参数
	ShowList();       // 也可以传入0个参数
	ShowList(1, 2, 3, "string");//也可以传入不同类型的参数
	return 0;
}

也可以通过sizeof来打印参数的个数:
在这里插入图片描述

为什么这里的调用时sizeof...(args)?


这里的sizeof...是一个操作符,用于计算参数包中元素的数量,其必须与参数包一起使用,而args是一个函数参数包的名称,表示在函数调用的时候传入的参数。
sizeof...是一个变体,专门用以计算参数包的大小,它只能与参数包一起使用。这个是C++11的规定。


原理:

  • 当你调用ShowList(1,2,3)的时候,args被推导为{1,2,3},这是一个包含三个参数的包。
  • sizeof...(args)计算的是args中的元素数量,结果为3
    在编译期间,编译器会根据传入的参数数量和类型来确定args的具体内容,并计算出数量。

但是我们仍不能方便的随机获取参数,同时可变参数的语法也不支持args[i],所以如何获取就值得我们探讨一番!

为什么可变参数模板的语法不支持args[i]呢?


  1. 可变参数模板是编译时编程
    可变参数模板是在编译期间展开的,编译器需要根据参数包的内容生成对应的代码。在编译期间,参数包中的参数是未知的,因此无法确定具体的下标。
  2. 参数包是一个整体
    在可变参数模板中,参数包args...是一个整体传递的,它代表了0个或者多个参数,无法像数组那样通过下标访问单个参数。
  3. 参数包需要展开
    要访问参数包中的单个参数,需要对参数包进行展开(后续会谈论)。常见的展开方式由递归展开、逗号表达式、初始化列表展开等。这些展开方式都是基于对参数包的遍历,而不是通过下标访问。
  4. 参数包的长度是编译常量
    通过sizeof...(args)可以在编译期间获取参数包长度。但这个长度是编译期常量,无法在运行时改变。因此,参数包的长度是固定的,无法像数组一样动态改变。

参数包的展开

递归式展开参数包

 递归展开参数包实际上是通过逐步剥离参数包中的元素来实现的。具体来说,对于下面的代码,编译器在编译的时候会根据传入的实参推导出模板参数的类型,并且生成相应的函数调用。每次递归调用都会减少参数包的大小,直到仅剩一个为止。

template<class T>
void ShowList(T value)
{
	cout << value << endl;
}

template<class T,class ...Args>
void ShowList(T value, Args... args)
{
	cout << value << " ";
	ShowList(args...);
}

int main()
{
	ShowList(1, 2, 3);
}

在这里插入图片描述
调用情况示例
当我们调用 ShowList(1, 2, 3) 时,调用栈的变化过程如下:

// 第一次调用
ShowList<int, int, int>(1, 2, 3);
    // 打印 1
    ShowList<int, int>(2, 3);
        // 打印 2
        ShowList<int>(3);
            // 打印 3
            // 递归终止

其次也可以这样写,写一个不带参的递归终止函数,当传入的args为空的时候,就会直接匹配第一个无模板无参的函数。
在这里插入图片描述

逗号表达式展开参数包

逗号表达式可以用来展开参数包,它的基本思路如下:

  1. 将逗号表达式的最后一个表达式设置为一个整型值,确保逗号表达式返回的是一个整型值。
  2. 将处理参数包中参数的动作封装成一个函数,将该函数的调用作为逗号表达式的第一个表达式。
  3. 在列表初始化时使用逗号表达式展开参数包。

这样一来,在执行逗号表达式时就会先调用处理函数处理对应的参数,然后再将逗号表达式中的最后一个整型值作为返回值来初始化整型数组。

下面是一个代码示例:



template<class T>
void PrintArg(T t)
{
	cout << t << " ";
}

template<class ... Args>
void ShowList(Args ... args)
{
	int arr[] = { (PrintArg(args),0)... };
	cout << endl;
}

int main()
{
	ShowList(1, 2, 3);
	cout << "---------------" << endl;
	ShowList("sad", 1);
	return 0;
}

在这里插入图片描述

通过初始化列表来初始化一个变长数组, {(printarg(args), 0)...}将会展开成((printarg(arg1),0),(printarg(arg2),0), (printarg(arg3),0), etc... ),由于是逗号表达式,在创建数组的过程中会先执行逗号表达式前面的部分printarg(args)打印出参数,也就是说在构造int数组的过程中就将参数包展开了,这个数组的目的纯粹是为了在数组构造的过程展开参数包。

那么为什么要返回一个 0 呢?


这是因为在列表初始化数组时,每个元素的表达式都需要返回一个值。如果不返回任何值,编译器会报错。所以,我们选择返回一个 0 作为占位符,因为这个值最终不会被使用到。关键是,逗号表达式会先执行左边的表达式,也就是打印参数,然后返回右边的值。
展开后,{ (PrintArg(arg1), 0), (PrintArg(arg2), 0), ... } 这个列表初始化会被展开成多个逗号表达式,每个表达式都会打印出对应的参数,并返回一个 0。这些 0 值会被用于初始化数组 arr
总之, 在使用逗号表达式展开参数包时,返回一个占位值是为了满足列表初始化的语法要求,而真正的目的是在逗号表达式的左边执行一些操作,比如打印参数。

emplace

emplace 的使用

emplace 是 C++11 中引入的一组成员函数,主要用于 STL 容器(如 vectorlistdeque 等),允许在容器中直接构造元素,而不是先创建对象再将其拷贝到容器中。

  1. 基本用法
    emplace 系列函数包括 emplace_backemplace_frontemplace,它们的基本用法如下:
  • emplace_back: 在容器的末尾构造一个新元素。
  • emplace_front: 在容器的开头构造一个新元素。
  • emplace: 在指定位置之前构造一个新元素。

示例代码:

#include <iostream>
#include <vector>
#include <string>

class Person {
public:
    Person(int age, const std::string& name) : _age(age), _name(name) {
        std::cout << "Constructing: " << name << std::endl;
    }
    int _age;
    std::string _name;
};

int main() {
    std::vector<Person> people;

    // 使用 emplace_back 直接构造对象
    people.emplace_back(25, "Alice");
    people.emplace_back(30, "Bob");

    // 使用 emplace 在指定位置构造对象
    people.emplace(people.begin() + 1, 22, "Charlie"); // 在 Bob 前面插入 Charlie

    for (const auto& person : people) {
        std::cout << person.name << " is " << person.age << " years old." << std::endl;
    }

    return 0;
}

在这里插入图片描述

emplace 的优势

效率更高的原因

  1. 直接构造:
    使用 emplace 系列函数时,传递的参数会直接用于构造对象,而不是先创建一个临时对象再将其拷贝到容器中。这减少了不必要的拷贝或移动构造。
  2. 避免临时对象:
    当使用 push_backinsert 时,通常需要创建一个临时对象,然后将其拷贝到容器中。这会导致额外的构造和析构开销。而 emplace 直接在容器内部构造对象,避免了这个过程。
  3. 支持参数包:
    emplace 支持可变参数模板,可以接受任意数量和类型的参数,这使得它在构造复杂对象时更加灵活。例如,可以直接传递多个参数来构造一个对象,而不需要先创建一个临时对象。

何时不优于传统插入

  • 如果传入的是左值对象或右值对象,emplace 的效率可能与传统的 push_backinsert 相当,因为在这些情况下,仍然可能会调用拷贝构造函数或移动构造函数。
  • emplace 的优势主要体现在构造新对象时,尤其是当传入参数包时,可以避免创建临时对象和拷贝构造。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2123113.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Visual Studio汇编代码高亮与自动补全

高亮插件&#xff1a;AsmDude (可以按照我的颜色进行设置&#xff0c;或者你自己改) 代码自动补全&#xff1a;CodeGeex (功能很多&#xff0c;支持的语言很多)&#xff0c;按Tab补全

Gitea Action注册runner

我的是gitea也可以和github 兼容&#xff0c;只是没有github 那么靓而已 安装一个gitea仓库 docker run -d --name gitea \-p3000:3000 -p2222:22 \-v /git/data:/data \ -v /etc/timezone:/etc/timezone:ro \-v /etc/localtime:/etc/localtime:ro \gitea/gitea:1.21.1setti…

嵌入式实时操作系统(RTOS):原理、应用与发展

摘要&#xff1a;本文围绕嵌入式实时操作系统&#xff08;RTOS&#xff09;展开。首先介绍嵌入式系统与实时操作系统的概念&#xff0c;阐述嵌入式 RTOS 的体系结构。接着分析其关键特性&#xff0c;包含任务管理&#xff08;如任务的创建与删除、调度、同步与通信&#xff09;…

基于SSM架构的农产品朔源系统

项目描述 这是一款基于SSM架构的农产品朔源系统 模块描述 农产品溯源系统 1、农产品管理 农产品列表 新增农产品 2、二维码管理 二维码列表 3、溯源管理 溯源列表 溯源图表 4、 企业管理 设置 添加企业 截图

ts复合流讲解

一、什么是复合流 复合流指的是一条音视频数据流中同时包含了音频ES和视频ES数据&#xff08;ES指的是从编码器出来的音视频裸流比如H264&#xff0c;AAC&#xff09;。在音视频开发中最常见的复合流一般是TS、MP4、flv等。TS和flv一般用于网络传输&#xff0c;MP4一般用于本地…

【区块链 + 人才服务】教育区域初中综合素质评价系统 | FISCO BCOS应用案例

根据国家及相关省份制定的高中阶段学校考试招生制度改革实施意见&#xff0c;全国部分地市将开展初中学生综合素质评 价工作。评价将从思想品德、学业水平、身心健康、艺术素养和社会实践五个维度来记录学生的发展过程。例如&#xff0c; 学生的党团社团活动参与情况、公益活动…

windows手工杀毒-关闭恶意弹窗

上篇回顾&#xff1a;windows手工杀毒-寻找可疑进程之网络连接-CSDN博客 上篇主要介绍了如何通过网络连接发现可疑进程。滥用公认端口的软件可能是可疑软件&#xff0c;因为占用公认端口&#xff0c;可能导致正常服务不能正常使用。可以查询ip或域名的相关情报信息&…

一篇文章了解Pytest单元测试框架

文章目录 1.Pytest是什么2.Pytest的安装3.Pytest快速入门4.Pytest文件规范5.常用的断言类型 1.Pytest是什么 pytest 是一个功能强大且灵活的 Python 测试框架,主要优点包括简洁易用、自动测试发现、丰富的插件生态系统、参数化测试、详细的断言错误信息、以及强大的 fixtures …

虚拟化数据恢复—虚拟机误还原几年前的快照怎么恢复数据?

虚拟化技术原理是将硬件虚拟化供不同的虚拟机使用&#xff0c;一台物理机上可以有多台虚拟机。人为误操作或者物理机故障会导致上层虚拟机不可用&#xff0c;甚至虚拟机里的重要数据丢失。下面给大家分享一个vmware虚拟化误操作还原快照的数据恢复案例。 虚拟化数据恢复环境&am…

基于GPT3打造你的专属的个人知识库

DocsGPT是一个基于GPT3的知识库平台&#xff0c;其支持训练、本地部署&#xff0c;并支持结果导出 https://github.com/arc53/DocsGPT DocsGPT本地部署 前置依赖&#xff1a; pippython3.8版本以上&#xff08;python3.7不支持langchain 0.0.100以上版本&#xff09;如使用ma…

【原理图PCB专题】案例:Cadence能设计一个没有管脚的器件吗?

在工作中突发奇想,如果Capture原理图中设计一个没有管脚的器件是不是可行?比如说有一些logo,如果在PCB绘制或完成时进行放置,那又怕会忘记。如果说在原理图就能放置,那么导入PCB后就可以直接变成器件的形式,是否就能完美的从设计上解决这个忘记放置的问题? 因此做一个案…

走进低代码表单开发(一):可视化表单数据源设计

在前文&#xff0c;我们已对勤研低代码平台的报表功能做了详细介绍。接下来&#xff0c;让我们深入探究低代码开发中最为常用的表单设计功能。一个完整的应用是由众多表单组合而成的&#xff0c;所以高效的表单设计在开发过程中起着至关重要的作用。让我们一同了解勤研低代码开…

从“N 号房”看Deepfake乱象,如何证明“我”不是我?

从“N 号房”看Deepfake乱象&#xff0c;如何证明“我”不是我&#xff1f; “9张玛丽莲梦露的照片里&#xff0c;有2张是AIGC伪造的&#xff0c;你能看出来哪张是假的吗&#xff1f;” 这是出现在外滩大会AI创新赛全球Deepfake攻防挑战赛的表演赛上激动人心的一刻。9月6日下…

[SC]Windows VS2022下配置SystemC环境

Windows VS2022下配置SystemC环境 一、安装Visual Studio 2022 下载地址&#xff1a;Visual Studio 2022 IDE - 适用于软件开发人员的编程工具 (microsoft.com) 进入Visual Studio Installer后选择“通用Windows平台开发”并勾选“可选”中的“通用Windows平台工具”。 二、下…

在Application中全局获取context

首先自定义一个application&#xff0c;继承Application&#xff0c;并在AndroidManifest.xml文件中配置它。 class TvApplication : Application() {companion object {Volatilevar context: Application? nullprivate setfun getContext(): Context {return context ?: t…

[笔记]电参数测量的现有方案[进行中...]

1.关键字&#xff1a; 电参数测量 Electrical Parameter Measurement 2.相关信息搜集 》》电参数测量仪是如何测量电压电流相位差的&#xff1f;对于变频器那种比较毛的波形&#xff0c;也能测量&#xff1f; 电参数测量仪测量电压电流相位差的方法主要依赖于其内部的高精度…

喜报!大模型助力产业升级,卓翼智能荣获科大讯飞AI开发者大赛冠军

8月9日&#xff0c;科大讯飞AI开发者大赛专场路演圆满落幕&#xff0c;这是一个面向全球开发者的人工智能竞赛平台。经过激烈角逐&#xff0c;《大模型助力下的航空航天产业转型升级赛道》最终有8支队伍进入决赛&#xff0c;专业评委从项目可行性、功能完备程度、创新性、商业价…

复仇时刻 华为的狙击还没结束

文&#xff5c;琥珀食酒社 作者 | 积溪 华为的复仇时刻已到啊 名场面即将再次上演 看过华为和苹果发布会的人 应该都有似曾相识的感觉 去年8月底 雷女士访华第二天 华为发布了Mate 60先锋计划 9月13日苹果发布iPhone 15 恰恰就在这天 华为咔嚓一下 又放出了大折叠屏…

用Python爬虫制作一个简易翻译器

我们通常是通过requestsBS4的方法来获取网页内容&#xff0c;这种方法导入模块较多&#xff0c;速度相对有点儿慢&#xff0c;此时我们可以用requests的post方法向指定服务器发送请求&#xff0c;获取数据后格式化为json&#xff0c;然后获取相关键值。这种方法用到了requests和…

电脑系统找不到mfc100u.dll,无法继续执行代码的多种解决方法

当你试图打开某些应用程序或游戏时&#xff0c;可能会收到“mfc100u.dll文件丢失”的错误消息。这个错误通常会使应用程序或游戏无法正常启动。以下关于mfc100u.dll丢失的解决方法的一些分析。 一.什么是mfc100u.dll文件 mfc100u.dll是Microsoft Visual Studio 2010应用程序使…