std::initialzer_list 与花括号{}数据列表
author: hjjdebug
date: 2025年 05月 22日 星期四 15:50:23 CST
descrip: std::initialzer_list 与花括号{}数据列表
文章目录
- 1.{数值列表}是什么?
- 1.1 数组初始化 时 , 称为数组初始化列表
- 1.2. 当用于容器时, 称为容器初始化列表
- 1.3. 对于结构体或类,{1,2,3,4,5} 可用于初始化成员变量
- 2. std::initializer_list 是什么?
- 3. 实现自己的initializer_list
- 3.1 可变参数包
- 3.2 实现自己的initializer
- 4.std::intializer_list 使用举例
- 5. 小结
我对如下语句发生了疑惑
std::initializer_list<int> data={1,2,3,4,5};
这到底是什么意思? 为什么它能用这样的语法?
1.{数值列表}是什么?
{1,2,3,4,5} 在c++中叫什么? 为什么用{}
1.1 数组初始化 时 , 称为数组初始化列表
例:
int arr[] = {1,2,3,4,5};
1.2. 当用于容器时, 称为容器初始化列表
std::vector<int> vec = {1,2,3,4,5}; // 初始化 vector
std::set<int> s = {1,2,3,4,5}; // 初始化 set
我也曾经常使用vector, 我把这种写法当成一种很自然的写法,
认为它天生就等价于把数值直接付给vector对象中的元素. 其实应该是想得太简单了.
1.3. 对于结构体或类,{1,2,3,4,5} 可用于初始化成员变量
struct Point { int x; int y; int z; };
Point p = {1, 2, 3}; // c语言方式结构体成员初始化, 顺序填充成员.
所以{1,2,3,4,5}没有统一标准, 与上下文有关, 还是叫花括号列表吧, 编译器没有对它规范约束.
2. std::initializer_list 是什么?
std::initializer_list是C++11标准库中的一个类模板
std::initializer_list本质上是一个轻量级的容器,用于存储相同类型的一组值,
并且这些值在其生命周期内是只读的。它通过存储一个指向常量数组的指针和元素个数来实现,
允许用户遍历其中的元素, 由于内部数据是const的,因此它仅能读取数据,不能修改其中的值
看了定义我还是不懂,它为什么能支持初始化列表?
std::initializer_list<int> data={1,2,3,4,5}; 为什么能够把数据列表放到对象中.
要想搞清楚它的工作原理, 莫若自己实现一个 initializer_list!
3. 实现自己的initializer_list
先认识一下可变参数包
3.1 可变参数包
可变参数包, 从函数调用的角度看, 就是有不定个数的参数,0个或者多个.
c++给我们想好了, 这可以用funcname(type… args) 来表达. type…就是任意个参数
由于类型也可变, 没关系, c++有类模板,支持各种类型, 类型可变,那也加个…表示可变.
即型如 template<typename… T_ARGS>
把2者连起来, 如下形式, 表示可变类型,可变参数的统一形式
template<typename… T_Args> void func(T_Args… args)
这就是可变参数模板. c++11 开始支持
其中, typename… 声明模板支持0个或多个类型参数
其中,T_Args… 表示函数可接受任意数量的参数,这些参数的类型由typename 指定
sizeof…(T_Args)可获取参数数量
args, 可变参数包名称,可以被展开.
举例:
#include <iostream>template<typename... Args>
void printAll(Args... args) {
// (std::cout << ... << args ) << std::endl; //左折叠表达式也可以((std::cout << args ), ...) << std::endl; //逗号加右折叠表达式也可以
}//,表达式用于连接多个操作
//右折叠表达式 ... 在参数包的右边, 右折叠从右向左结合,不过本例没有结合性
template<typename T, typename... Args>
void printWithComma(T first, Args... args) {std::cout << first;((std::cout << ", " << args), ...); //逗号加右折叠表达式std::cout << std::endl;
}
int main() {printWithComma(1, 2.5, "hello"); // 输出:1,2.5,helloprintAll("A", "B", "C"); // 输出:ABCreturn 0;
}
执行:
$ ./temp
1,2.5,hello
ABC
说明:
(std::cout << … << args) 是C++17引入的折叠表达式(Fold Expression)语法
… ,参数包展开符, 必需在折叠表达式中使用才有效
args,可变参数包, 在折叠式中代表一个参数.
3.2 实现自己的initializer
实现代码:
#include <stdio.h>
#include <vector>template<typename T>
class MyInitializer
{std::vector<T> data_;public:template<typename... T_Args>MyInitializer(T_Args&&... args) {data_.reserve(sizeof...(T_Args)); //调整vector 容量//emplace安置 emplace_back尾部安置比push_back效率更高,免copy(data_.emplace_back(std::forward<T_Args>(args)), ...); //折叠表达式,...表示展开参数包}size_t size() const { return data_.size(); }auto begin() const { return data_.begin(); }auto end() const { return data_.end(); }
};int main()
{MyInitializer<int> initList{1, 2, 3};for(auto it=initList.begin(); it!=initList.end();it++){printf("%d\n",*it);}return 0;
}
执行结果:
$ ./temp2
1
2
3
为什么可以接受 {1,2,3} 初始化列表?
因为c++11 始引入了可变参数包语法支持 template<typename… T_Args>,
可支持任意类型,可变参数0个或多个的语法. 能够逐个取到每个参数.
类对象初始化中的{1,2,3} 认为是3个整形数
函数调用中的(1,2.5,“hello”)认为是1个整数,1个浮点数,1个字符串的列表数据.
看起来{},()所起的作用还是有点混乱, 不如逗号,是数据的分割意义很明确.
初始化器为什么要求初始化列表是**“一种"数据类型? 而不能是"多种”**数据类型.
因为它的实现使用了数据容器,类似于vector, 数据容器要求数据是一种类型.
std::initializer_list<int> data={1,2,3,4,5}; 语句解释.
{1,2,3,4,5}是一个花括号列表数据,它会被隐士转换为一个initializer_list 的无名对象,
对象的构建方式当然是它的构造函数, 其构造函数支持列表数据初始化.
然后通过赋值构造函数把无名对象赋值给对象 data
有了这些基础,就可以使用std::initializer_list 标准库模板函数了.
下面给个测试例,体会一下c语言的做法传递数组,与c++的做法传递初始化列表对象(initializer_list obj)的差别.
4.std::intializer_list 使用举例
#include <initializer_list>
#include <vector>
#include <iostream>//定义一个int容器, 用初始化列表接受初始化数据
class IntContainer
{
public://构造函数中使用initializer_list对象做参数//vector 支持 list对象初始化IntContainer(std::initializer_list<int> list_obj) : m_data(list_obj){}void print() const {for (auto elem : m_data) {std::cout << elem << ' ';}std::cout << std::endl;}
private:std::vector<int> m_data;
};//函数中使用
int sum_obj(std::initializer_list<int> list_obj) //对象自带大小.
{int total = 0;for (auto& num : list_obj) // 传递对象很安全,对象甚至不用大小,直接枚举.{total += num;}return total;
}void printArr(int *arr, int size) //传递数组不安全,size出错就出错.
{for(int i=0;i<size;i++){printf("%d ",arr[i]);}printf("\n");
}int sum_c(int *arr, int size)
{int total = 0;for (int i=0;i<size;i++) //size 很危险,万一传错参数,arr[i]要出错!{total += arr[i];}return total;
}
int main() {std::initializer_list list_obj={1,2,3,4,5}; //会发生隐含转换,从数值列表到无名list对象,然后再调用list的赋值构造IntContainer container_obj = list_obj; //先创建无名container对象,再调用默认的赋值构造函数(浅copy构造函数)
// IntContainer container_obj = {1, 2, 3, 4, 5}; //一步也可以,过程是数值列表->无名list对象->无名container对象->container_obj对象.container_obj.print(); // 输出:1 2 3 4 5int n = sum_obj({1,2,3,4,5}); //隐含转换,从数值列表到无名list对象,传递给函数参数,传递的是对象,安全!printf("sum_obj:%d\n",n);int arr[] = { 1,2,3,4,5}; // c语言的做法, 用数组. 但元素的个数就是一个风险因素,传错就有麻烦!
#define SIZE sizeof(arr)/sizeof(int)printArr(arr,SIZE); //c函数传递SIZE 不安全,万一传错就有风险!n=sum_c(arr, SIZE); //c函数传递SIZE 不安全,万一传错就有风险!printf("sum_c:%d\n",n);return 0;
}
执行结果:
$ ./tt
1 2 3 4 5
sum_obj:15
1 2 3 4 5
sum_c:15
5. 小结
总之:
用对象传递安全,因为对象自带大小. 用数组传递不安全.用数组还要传递大小,有出错风险.
相关文章:
std::initialzer_list 与花括号{}数据列表
author: hjjdebug date: 2025年 05月 22日 星期四 15:50:23 CST descrip: std::initialzer_list 与花括号{}数据列表 文章目录 1.{数值列表}是什么?1.1 数组初始化 时 , 称为数组初始化列表1.2. 当用于容器时, 称为容器初始化列表1.3. 对于结构体或类,{…...
萤石云实际视频实时接入(生产环境)
萤石云视频接入 本示例可用于实际接入萤石云开放平台视频,同时支持音频输入和输出。 实际优化内容 1.动态获取token 2.切换各公司和车间时,自动重新初始化播放器 let EZUIKit null; // 第三方库引用 let EZUIKitPlayers []; // 播放器实例数组 le…...
QT中常用的类
Qt 是一个功能强大的跨平台框架,提供了丰富的类库来开发 GUI 和应用程序。以下是 Qt 中常用的核心类,按模块分类整理: 1. GUI 和窗口管理 类名用途示例场景QWidget所有 GUI 控件的基类(按钮、窗口等&…...
Docker系列(四):容器操作全栈技术指南 --- 从入门到生产级管控
引言 本指南以全链路视角拆解Docker技术栈,通过四大核心模块构建从入门到进阶的知识体系,助您系统性掌握容器化落地的关键能力。 容器生命周期管理(一)从创建、启停到资源清理,夯实容器操作的基础语法与核心场景&…...
poppler_path 是用于 Python 库如 pdf2image 进行 PDF 转换时
poppler_path 是用于 Python 库如 pdf2image 进行 PDF 转换时指定 Poppler 可执行文件路径的参数。为了让程序正常工作,需要先安装 Poppler,并配置环境变量或在代码中设置 poppler_path。 以下是 Poppler 的安装与环境变量配置方法,按操作系…...
鸿蒙OSUniApp 开发的多图浏览器组件#三方框架 #Uniapp
使用 UniApp 开发的多图浏览器组件 在移动应用开发中,图片浏览器是非常常见且实用的功能,尤其是在社交、资讯、电商等场景下,用户对多图浏览体验的要求越来越高。随着 HarmonyOS(鸿蒙)生态的不断壮大,开发…...

MongoDB 错误处理与调试完全指南:从入门到精通
在当今数据驱动的世界中,MongoDB 作为最流行的 NoSQL 数据库之一,因其灵活的数据模型和强大的扩展能力而广受开发者喜爱。然而,与任何复杂系统一样,在使用 MongoDB 过程中难免会遇到各种错误和性能问题。本文将全面介绍 MongoDB 的…...
React从基础入门到高级实战:React 核心技术 - 表单处理与验证深度指南
React 表单处理与验证深度指南 在现代 Web 应用中,表单是用户与应用交互的核心方式之一。无论是注册、登录、结账还是数据提交,表单都扮演着至关重要的角色。React 作为一款流行的前端框架,提供了多种处理表单的工具和方法,帮助开…...

【C++】stack,queue和priority_queue(优先级队列)
文章目录 前言一、栈(stack)和队列(queue)的相关接口1.栈的相关接口2.队列的相关接口 二、栈(stack)和队列(queue)的模拟实现1.stack的模拟实现2.queue的模拟实现 三、priority_queu…...

ubuntu中上传项目至GitHub仓库教程
一、到github官网注册用户 1.注册用户 地址:https://github.com/ 2.安装Git 打开终端,输入指令git,检查是否已安装Git 如果没有安装就输入指令 sudo apt-get install git 二、上传项目到github 1.创建项目仓库 进入github主页,点击号…...
[Java实战]Spring Boot整合达梦数据库连接池配置(三十四)
[Java实战]Spring Boot整合达梦数据库连接池配置(三十四) 一、HikariCP连接池配置(默认) 1. 基础配置(application.yml) spring:datasource:driver-class-name: dm.jdbc.driver.DmDriverurl: jdbc:dm://…...

windows 下用yolov5 训练模型 给到opencv 使用
windows 使用yolov5训练模型,之后opencv加载模型进行推理。 一,搭建环境 安装 Anaconda 二,创建虚拟环境并安装yolov5 conda create -n yolov5 python3.9 -y conda activate yolov5 git clone https://github.com/ultralytics/yolov5 cd …...

Spark集群架构解析:核心组件与Standalone、YARN模式深度对比(AM,Container,Driver,Executor)
一、核心组件定义与关系拆解 1. ApplicationMaster(AM) 定义:YARN 框架中的应用管理器,每个应用程序(如 Spark 作业)对应一个 AM。职责: 向 YARN 的 ResourceManager 申请资源(Con…...

Linux Kernel调试:强大的printk(二)
前言 如果你对printk的基本用法还不熟悉,请先阅读: Linux Kernel调试:强大的printk(一) 上一篇Linux Kernel调试:强大的printk(一)我们介绍了printk的基础知识和基本用法…...

Kafka Kraft模式集群 + ssl
文章目录 启用集群资源规划准备证书创建相关文件夹配置文件启动各Kafka节点 故障转移测试spring boot集成 启用集群 配置集群时关键就是提前梳理好需要的网络资源,完成对应server.properties文件的配置。在执行前先把这些梳理好,可以方便后面的配置&…...

[crxjs]自己创建一个浏览器插件
参考官方 https://crxjs.dev/vite-plugin/getting-started/vue/create-project 按照流程操作会失败的原因 是因为跨域的问题, 在此处添加 server: {host: "localhost",port: 5173,cors: true,headers: {"Access-Control-Allow-Origin": "*",}…...

类的设计模式——单例、工厂以及建造者模式
1.单例模式 1.1 饿汉模式 单例模式:一个类只能创建一个对象,这个设计模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。 饿汉模式指在程序初始化时就创建一个唯一的实例对象。适用…...

STM32之看门狗(IWDG)
一、看门狗外设的原理与应用 背景说明 随着单片机的发展,单片机在家用电器、工业自动化、生产过程控制、智能仪器仪表等领域的应用越来越广泛。然而处于同一电力系统中的各种电气设备通过电或磁的联系彼此紧密相连,相互影响,由于运行方式的…...
PyTorch实现MLP信用评分模型全流程
知识点回顾: 过拟合的判断:测试集和训练集同步打印指标模型的保存和加载 仅保存权重保存权重和模型保存全部信息checkpoint,还包含训练状态 早停策略 浙大疏锦行 import torch import torch.nn as nn import torch.optim as optim from skle…...
语音识别——文本转语音
python自带的pytts说话人的声音比较机械,edge-tts提供了更自然的语音合成效果,支持多种语音选择。 项目地址:GitHub - rany2/edge-tts: Use Microsoft Edges online text-to-speech service from Python WITHOUT needing Microsoft Edge or …...

跟着华为去变革 ——读《常变与长青》有感
《常变与长青》,是华为郭平总2024年上市的著作。走进这本书,我们能够清晰看到华为30多年的成长过程和伴随期间的变革历程:从一家设备代理商开始,起步蹒跚,砥砺前行,在闯过一个又一个磨难之后,成…...

图像分割技术的实现与比较分析
引言 图像分割是计算机视觉领域中的一项基础技术,其目标是将数字图像划分为多个图像子区域(像素的集合),以简化图像表示,便于后续分析和理解。在医学影像、遥感图像分析、自动驾驶、工业检测等众多领域,图…...

node.js配置变量
一、下载安装包 1、官网下载 大家可以在官网下载,适合自己电脑以及项目的需要的版本。 二、node.js安装 1、安装 双击下载的安装包文件,通常为 .exe 或 .msi 格式(Windows)或 .dmg 格式(Mac)。系统会…...

Ubuntu+Docker+内网穿透:保姆级教程实现安卓开发环境远程部署
文章目录 前言1. 虚拟化环境检查2. Android 模拟器部署3. Ubuntu安装Cpolar4. 配置公网地址5. 远程访问小结 6. 固定Cpolar公网地址7. 固定地址访问 前言 本文将详细介绍一种创新性的云开发架构:基于Ubuntu系统构建Android仿真容器环境,并集成安全隧道技…...
为什么需要清除浮动?清除浮动的方式有哪些?
导语: 在前端面试中,“清除浮动”几乎是每位面试官都会问到的基础题。虽然浮动已经不如 Flex 和 Grid 那么常用了,但它在许多老项目中仍然占有一席之地。理解浮动的机制、掌握清除浮动的方式,是面试中体现你前端基础扎实度的关键点。 一、面试主题概述 浮动(float)最初是…...
计算机网络学习20250526
SMTP——简单邮件传输协议 TCP 端口号:25 Alice给Bob发送邮件过程: Alice使用邮件代理程序写邮件给Bob用户代理把报文发给邮件服务器,放入报文队列中邮件服务器上SMTP客户端建立与Bob服务器上SMTP服务器的TCP连接经过初始的握手后ÿ…...
ArkUI:鸿蒙应用响应式与组件化开发指南(一)
文章目录 引言1.ArkUI核心能力概览1.1状态驱动视图1.2组件化:构建可复用UI 2.状态管理:从单一组件到全局共享2.1 状态装饰器2.2 状态传递模式对比 引言 鸿蒙生态正催生应用开发的新范式。作为面向全场景的分布式操作系统,鸿蒙的北向应用开发…...
YOLOv11改进 | Neck篇 | 双向特征金字塔网络BiFPN助力YOLOv11有效涨点
YOLOv11改进 | Neck篇 | 双向特征金字塔网络BiFPN助力YOLOv11有效涨点 引言 目标检测领域的最新进展表明,特征金字塔网络(FPN)的设计对模型性能具有决定性影响。本文详细介绍如何将**双向特征金字塔网络(BiFPN)**集成到YOLOv11的Neck部分,通过改进的多尺度特征融合机制…...
C/C++的OpenCV 进行轮廓提取
使用 C/C的OpenCV 进行轮廓提取 轮廓可以简单地描述为连接所有具有相同颜色或强度的连续点(沿着边界)的曲线。轮廓是形状分析以及对象检测和识别的有用工具。OpenCV 提供了非常方便的函数来查找和绘制轮廓。 本文将指导您完成使用 C 和 OpenCV 库从图像…...

计算机网络总结(物理层,链路层)
目录 第一章 概述 1.基本概念 2.- C/S模式,B/S模式,P2P模式 3.- LAN,WAN,MAN,PAN的划分 4.电路交换与分组交换,数据报交换和虚电路交换 第二章 物理层 1.信号编码:不归零编码,曼切斯特编码 2.几种复用技术的特…...