【C++11】initializer_list、可变参数模板详解

目录

  • 一、统一的列表初始化
    • 1.{}初始化
    • 2.initializer_list
  • 二、可变模版参数
    • 1.可变模版参数简介
    • 2.模板参数包展开的方式
    • 3.示例 emplace_back

一、统一的列表初始化

1.{}初始化

在C++98标准中,允许使用花括号{}对数组或者结构体元素进行统一的列表初始值设定

int array1[] = { 1, 2, 3, 4, 5 };
struct Point
{int _x;int _y;
};
Point p = { 1, 2 };

C++11扩大了用大括号括起的列表(初始化列表)的使用范围

使其可用于所有的内置类型和用户自定义的类型,使用初始化列表时,可添加等号(=),也可不添加

创建对象时也可以使用列表初始化方式调用构造函数初始化

struct Point
{int _x;int _y;
};class Date
{
public:Date(int year, int month, int day):_year(year), _month(month), _day(day){cout << "Date(int year, int month, int day)" << endl;}
private:int _year;int _month;int _day;
};int main()
{int x1 = 1;int x2{ 2 };int array1[]{ 1, 2, 3, 4, 5 };int array2[5]{ 0 };//结构体Point p{ 1, 2 };//new表达式int* pa = new int[4] { 0 };//类Date d1(2022, 1, 1);// C++11支持列表初始化,这里会调用构造函数初始化Date d2{ 2022, 1, 2 };Date d3 = { 2022, 1, 3 };return 0;
}

总结一句话就是,C++11想让“一切皆可{}初始化”

2.initializer_list

在C++11中,std::initializer_list 是一个模板类,它提供了一种方式来初始化对象或容器,使得代码更加直观和易于书写。

std::initializer_list 允许你使用花括号 {} 包围的列表来初始化对象

这种方式与数组的初始化方式相似,但更加灵活,因为它可以用于任何类型的对象,包括自定义类型

std::initializer_list 的定义位于头文件 <initializer_list> 中。

它通常作为构造函数的一个参数,用于接收一个初始化列表。

C++11对STL中的不少容器就增加std::initializer_list作为参数的构造函数,这样初始化容器对象就更方便了。也可以作为operator=的参数,这样就可以用大括号赋值。

int main()
{vector<int> v = { 1,2,3,4 };list<int> lt = { 1,2 };// 这里{"sort", "排序"}会先初始化构造一个pair对象map<string, string> dict = { {"sort", "排序"}, {"insert", "插入"} };// 使用大括号对容器赋值v = { 10, 20, 30 };return 0;
}

下面是一个简单的例子
展示了如何定义一个接受std::initializer_list 的构造函数,并在创建对象时使用它:

#include <iostream>  
#include <initializer_list>  
#include <vector>  class MyClass {  
public:  std::vector<int> values;  // 构造函数,接受一个 initializer_list<int>  MyClass(std::initializer_list<int> init_list) : values(init_list) {}  void print() {  for (int val : values) {  std::cout << val << " ";  }  std::cout << std::endl;  }  
};  int main() {  MyClass obj = {1, 2, 3, 4, 5}; // 使用 initializer_list 初始化对象  obj.print(); // 输出: 1 2 3 4 5  return 0;  
}

二、可变模版参数

1.可变模版参数简介

在C++98及这前的版本中类模版和函数模版中只能含固定数量的模版参数
C++11的新特性可变参数模板能够让您创建可以接受可变参数的函数模板和类模板

模板参数包是一个特殊的模板参数,它表示零个或多个模板参数。在函数模板或类模板的定义中,可以使用省略号(…)来声明一个模板参数包。

// Args是一个模板参数包,args是一个函数形参参数包
// 声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。
template<typename... Args>  
void print(Args... args) {  // 这里可以使用递归或循环来展开参数包  // 但更常见的是利用C++11的初始化列表展开(initializer list expansion)  // 在这里,我们只是简单地使用它们来演示  // 注意:实际打印需要更复杂的逻辑  // ...  
}  // 使用  
print(1, 2.3, "Hello")template<typename... Types>  
class Tuple {  
public:  // 类体,可能包含存储每种类型的成员变量  // 注意:这只是一个框架,实际实现需要更复杂的逻辑  
};  // 使用  
Tuple<int, double, std::string> myTuple;

2.模板参数包展开的方式

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

1.递归
通过编写一个递归的模板函数,每次递归调用时从参数包中取出一个参数,直到参数包为空。这种方法需要定义一个递归终止函数,用于处理参数包为空的情况

template<typename T, typename... Args>  
void print(T first, Args... args) {  std::cout << first << " ";  if constexpr (sizeof...(args) > 0) {  print(args...); // 递归调用  }  std::cout << std::endl;  
}  template<typename T>  
void print(T last) {  std::cout << last << std::endl;  
}

2.逗号表达式
这种展开参数包的方式,不需要通过递归终止函数,是直接expand函数体中展开的, printarg不是一个递归终止函数,只是一个处理参数包中每一个参数的函数。

这种就地展开参数包的方式实现的关键是逗号表达式。我们知道逗号表达式会按顺序执行逗号前面的表达式。

template<typename... Args>  
void print(Args... args) {  (void)std::initializer_list<int>{(std::cout << args << " ", 0)...};  std::cout << std::endl;  
}

3.示例 emplace_back

template <class... Args>
void emplace_back (Args&&... args);

我们看到的emplace系列的接口,支持模板的可变参数,并且万能引用。那么相对insert和push_back,emplace系列接口的优势到底在哪里?
1. 直接在容器内构造对象
emplace系列接口允许直接在容器内部构造对象,而不需要先构造一个对象然后再将其插入容器中。这避免了额外的复制或移动构造函数调用,从而提高了性能。特别是当对象构造开销较大或者对象具有移动语义但不支持复制时,emplace的优势更加明显。
2. 模板可变参数支持
emplace接口支持模板的可变参数,这意味着它可以接受任意数量和类型的参数,并将这些参数直接传递给容器内部对象的构造函数。这使得emplace接口更加灵活,能够适应不同种类的对象构造需求。
3. 万能引用(Perfect Forwarding)
emplace接口通常利用C++11引入的完美转发(Perfect Forwarding)机制,通过std::forward等函数确保传递给构造函数的参数能够保持其原始的类型和值类别(左值或右值)。这意味着如果传递给emplace的是右值,那么对象将在容器内部以移动语义构造,进一步提高了效率。
4. 减少内存分配和拷贝次数
在某些情况下,使用emplace可以避免不必要的内存分配和对象拷贝。例如,在std::vector中,当向容器末尾添加元素且容量足够时,emplace_back可以直接在预留的空间内构造新对象,而无需重新分配内存或移动其他元素。

#include <vector>  
#include <string>  class MyClass {  
public:  MyClass(int x, std::string s) : x_(x), s_(std::move(s)) {}  // ... 其他成员函数 ...  private:  int x_;  std::string s_;  
};  int main() {  std::vector<MyClass> vec;  // 使用 push_back,需要先构造一个临时对象  vec.push_back(MyClass(1, "hello")); // 这里会调用 MyClass 的拷贝或移动构造函数  // 使用 emplace_back,直接在容器内构造对象  vec.emplace_back(2, "world"); // 这里会直接调用 MyClass 的构造函数  // ... 其他操作 ...  
}

在上面的示例中,emplace_back直接在vec的末尾构造了一个MyClass对象,而push_back则需要先构造一个临时的MyClass对象,然后再将其插入到vec中。这可能导致额外的拷贝或移动构造函数调用。

综上所述,emplace系列接口相比insert接口在性能、灵活性和减少内存分配/拷贝次数方面提供了显著的优势。因此,在需要向容器中添加新元素时,如果可能的话,应该优先考虑使用emplace接口。

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

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

相关文章

Ubuntu24开机黑屏,VMware卡死,虚拟机繁忙解决方案

文章目录 可能出现的异常情况问题原因黑屏解决方案开机黑屏解决方案一开机黑屏解决方案二进入终端的方法 可能出现的异常情况 安装时无法启动&#xff0c;无法正常执行安装程序安装后启动黑屏启动后能够进入登陆界面&#xff0c;登陆后黑屏黑屏后VMware整个卡死&#xff0c;Ub…

数学建模学习(111):改进遗传算法(引入模拟退火、轮盘赌和网格搜索)求解JSP问题

文章目录 一、车间调度问题1.1目前处理方法1.2简单案例 二、基于改进遗传算法求解车间调度2.1车间调度背景介绍2.2遗传算法介绍2.2.1基本流程2.2.2遗传算法的基本操作和公式2.2.3遗传算法的优势2.2.4遗传算法的不足 2.3讲解本文思路及代码2.4算法执行结果&#xff1a; 三、本文…

怎么使用动态IP地址上网

如何设置动态IP地址上网&#xff1f; 设置动态IP地址上网的步骤如下&#xff1a; 一、了解动态IP地址 动态IP地址是由网络服务提供商&#xff08;ISP&#xff09;动态分配给用户的IP地址&#xff0c;它会根据用户的需求和网络情况实时改变。相比于静态IP地址&#xff0c;动态…

基于术语词典干预的机器翻译挑战赛笔记 Task3 #Datawhale AI 夏令营

书接上回&#xff0c;上回在这捏&#xff1a; 基于术语词典干预的机器翻译挑战赛笔记Task2 #Datawhale AI 夏令营-CSDN博客文章浏览阅读223次&#xff0c;点赞10次&#xff0c;收藏5次。基于术语词典干预的机器翻译挑战赛笔记Task2https://blog.csdn.net/qq_23311271/article/…

状压dp,D - Grid Puzzle

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 D - Grid Puzzle 二、解题报告 1、思路分析 贪心做法看不懂&#xff08;为什么我赛时要跟贪心过不去啊&#xff09; 这个题麻烦在这个case&#xff1a;2 4 4 2&#xff0c;我们可以清除三次2x2得到 但是我…

【NLP】Jieba中文分词

Jieba分词是一个用于中文文本分词的开源工具。它可以将一段连续的中文文本切分成一个一个的词语&#xff0c;这对于中文自然语言处理&#xff08;NLP&#xff09;任务如文本分类、情感分析、机器翻译等非常重要。Jieba分词具有以下特点&#xff1a; 支持三种分词模式&#xff1…

【机器学习】FlyFlowerSong【人工智能】资源指南

一、引言 FlyFlowerSong是一个创新的音乐合成与处理项目&#xff0c;它利用先进的机器学习算法&#xff0c;为用户提供了一个简单而有趣的音乐创作平台。作为人工智能领域的技术自媒体创作者&#xff0c;我整理了关于FlyFlowerSong的完整教程、论文复现指南以及demo项目源代码…

前端post提交一次会有两次请求?

1 问题&#xff1a;前端post只提交一次会有两次请求&#xff1f; 前端post只提交一次会有两次请求&#xff1f;如下图&#xff1a; 这里是执行了两次post提交&#xff0c;每个post都有两次&#xff08;一次是preflight以options方式&#xff0c;一次是xhr&#xff0c;原本…

安装docker-18.06报错Error: libseccomp conflicts with docker-18.06

安装dockers报错 Error: libseccomp conflicts with docker-18.06.1ce-6.amzn2.x86_64 You could try using --skip-broken to work around the problem You could try running: rpm -Va --nofiles --nodigest 这个是libseccomp版本跟docker版本不对&#xff0c;要升级docker版…

【笔记-软考】架构演化

Author&#xff1a;赵志乾 Date&#xff1a;2024-07-20 Declaration&#xff1a;All Right Reserved&#xff01;&#xff01;&#xff01; 1. 概念 架构都会经历初始设计、实际使用、修改完善和退化弃用的过程&#xff0c;其中修改完善即为架构的演化过程&#xff0c;其演化的…

STM32+USART串口(1)

GPIO口的复用功能是有对应的&#xff0c;作USART使用的话要选择对应的GPIO&#xff1b;可以参考引脚定义&#xff1b; &#xff08;1&#xff09;串口通信分为&#xff1a;串行通信和并行通信&#xff1b; &#xff08;2&#xff09;通信波特率 &#xff1a;通常用波特率&…

Yolo-World网络模型结构及原理分析(三)——RepVL-PAN

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言1. 网络结构2. 特征融合3. 文本引导&#xff08;Text-guided&#xff09;4. 图像池化注意力&#xff08;Image-Pooling Attention&#xff09;5. 区域文本匹配&…

springboot+vue+mybatis校园热点新闻系统+PPT+论文+讲解+售后

21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识&#xff0c;科学化的管理&#xff0c;使信息存储达到…

Tomcat长连接源码解析

长连接: 客户端发送Http请求至服务端&#xff0c;请求发送完之后socket连接不断开&#xff0c;可以继续接收下一个Http请求并且解析返回。接手并解析这些Http请求的时候socket连接不断开&#xff0c;这种过程被称为长连接。 需要注意的点就在于&#xff0c;在满足什么条件的情况…

如何在 Windows 上恢复丢失或删除的文件

意外删除重要文件或文档的历史与 Windows 本身一样悠久&#xff0c;这就是为什么有许多内置方法来恢复它。从深入回收站到挖掘 Microsoft 的 Windows 文件恢复实用程序&#xff0c;以下是如何在 Windows 中恢复丢失和删除的文件。 检查回收站 Windows 帮助您恢复已删除并需要再…

IO多路复用之poll、epoll和select区分

epoll和select 假设你在大学读书&#xff0c;住的宿舍楼有很多间房间&#xff0c;你的朋友要来找你。 select版宿管大妈就会带着你的朋友挨个房间去找&#xff0c;直到找到你为止。 而epoll版宿管大妈会先记下每位同学的房间号&#xff0c; 你的朋友来时&#xff0c;只需告诉你…

【Langchain大语言模型开发教程】链

&#x1f517; LangChain for LLM Application Development - DeepLearning.AI 学习目标 1、LLMChain 2、Sequential Chains 3、Router Chain LLMChain import warnings warnings.filterwarnings(ignore) import os import pandas as pdfrom dotenv import load_dotenv, find…

德国汉堡大学、清华大学联合英国布里斯托机器人实验室的研究工作分享:基于视觉遥操作的多指机械手灵巧操作

德国汉堡大学&#xff08;张建伟院士团队&#xff09;、清华大学&#xff08;孙富春教授和方斌&#xff09;联合英国布里斯托机器人实验室等单位在基于视觉信息遥操作的多指机械手灵巧操作研究方面取得进展。该工作得到了德国科学基金会&#xff08;DFG&#xff09;与中国国家自…

Git分支管理基本原理

原文全文详见个人博客&#xff1a; Git分支管理基本原理上文已讨论过svn分支管理的基本原理&#xff0c;本文将继续探讨Git分支管理的基本原理&#xff0c;以便后续进行进一步的理解和对比&#xff1a;https://www.coderli.com/git-branch-method/【Java学习交流(982860385)】…

Linux journalctl日志太长,如何倒序查看journalctl --reverse,journalctl -xeu

文章目录 需求实验方法一方法二 需求 Linux journalctl日志太长&#xff0c;如何倒序查看 我们通常关心的是最近的日志&#xff0c;但是每次打开日志都是按时间先后顺序显示的&#xff0c;如何倒序查看&#xff0c;请看下面&#xff1a; 实验 方法一 journalctl 命令默认按…