一文速通 std::initializer_list

ops/2025/3/4 5:09:21/

目录

  • 用途
  • 原理
  • 加深理解 {} 和 initializer_list
    • 为什么不可以?
    • 该怎么做

用途

初始化未显示指定长度的数组,存在语法糖:

int arr[] { 1, 2, 3 };

C++11开始,引入了**“统一初始化”**的概念STL 容器拥有类似的初始化能力,可以使用 **{}**这种通用的语法在任何需要初始化的地方。

原因:STL容器通过使用std::initializer_list 负责接收初始化列表。

vector( std::initializer_list<T> init,const Allocator& alloc = Allocator() );
map( std::initializer_list<value_type> init,const Compare& comp = Compare(),const Allocator& alloc = Allocator() );

大致用法:

#include <initializer_list>std::vector<int> vec = {1, 2, 3, 4, 5};
std::vector vec = {1, 2, 3, 4, 5}; // CTAD
std::map<std::string, int> m = {{ "1", 1 }, { "2", 2 }, { "3", 3 }
};
std::set<int> s = { 1, 2, 3 };

当然,可以通过支持initializer_list,来让自定义容器允许“统一初始化”:

class FooVector {std::vector<int> content_;
public:FooVector(std::initializer_list<int> list) {for (auto it = list.begin(); it != list.end(); ++it) {content_.push_back(*it);}}
};FooVector foo_1 = {1, 2, 3, 4, 5};

原理

std::initializer_list 是轻量级的类模板

namespace std {template<class E> class initializer_list {public:using value_type      = E;using reference       = const E&;using const_reference = const E&;using size_type       = size_t;using iterator        = const E*;using const_iterator  = const E*;constexpr initializer_list() noexcept;constexpr size_t size() const noexcept;     // number of elementsconstexpr const E* begin() const noexcept;  // first elementconstexpr const E* end() const noexcept;    // one past the last element};// initializer list range accesstemplate<class E> constexpr const E* begin(initializer_list<E> il) noexcept;template<class E> constexpr const E* end(initializer_list<E> il) noexcept;
}
  • 可接收任意长度的初始化列表,但要求元素必须 是/可转换为 同种类型 T。

  • 内部定义了

    • iterator 等容器必需的概念。
    • 成员函数: size()、 begin()、 end()。
std::initializer_list<int> list;
size_t n = list.size(); // n == 0
list = { 1, 2, 3, 4, 5 };
n = list.size(); // n == 5
  • 不负责保存列表中元素的拷贝。看做保存元素的引用,在持有对象的生存期结束之前完成传递。
// 不应该像这样使用:
std::initializer_list<int> func(void) {int a = 1, b = 2;return { a, b }; // a、 b 在返回时并没有被拷贝
}
// 更好的做法, 构造接收initializer_list作为参数的对象。
//  constexpr vector(initializer_list<T>, const Allocator& = Allocator());
std::vector<int> func(void)
{int a = 1, b = 2;// vector构造函数接收std::initializer_list作为参数return {a, b};
}

list_119">加深理解 {} 和 initializer_list

c++中为什么push_back({1,2})可以,emplace_back({1,2})会报错?

明明 vector 存在构造函数,以initializer_list为入参。

  template<class T, class Allocator = allocator<T>>class vector {public:constexpr vector(initializer_list<T>, const Allocator& = Allocator());

为什么push_back({1,2})可以,emplace_back({1,2})会报错?

vector<vector<int>> a;
a.push_back({1,2}); 	// 可以
a.emplace_back({1,2});	// 报错

为什么不可以?

答:{1,2} 本身什么都不是。

{}花括号初始化器列表不是表达式因此它没有类型decltype({1,2})非良构

{1, 2} 变身 std::initializer_list<int>是有条件的:

// 1. 显式声明 initializer_list
std::initializer_list<int> x1 {1, 2};
std::initializer_list x1 {1, 2}; // CTAD模板类型推导// 2. 用 auto 推导
// 将花括号初始化器列表推导为std::initializer_list
auto x2 = { 1,2,3,4,5,6 };// braced initialization of a variable declared with a placeholder type but without `=` requires exactly one element inside the bracesC/C++(2663
// auto x2 { 1,2,3,4,5,6 }; // 无法推导// 3. 函数调用入参为 std::initializer_list
void func(std::initializer_list<int>) {}
func({1, 2, 3});

emplace_back本身是个模板,所以就形成了一个死锁的局面:

  • emplace_back 实例化出接受 std::initializer_list 的版本的前提是 {1,2} 变身。
  • {1,2} 变身的前提是 emplace_back 实例化出接受 std::initializer_list 的版本。
    // modifierstemplate<class... Args>constexpr reference emplace_back(Args&&... args);constexpr void push_back(const T& x);constexpr void push_back(T&& x);

既然这么麻烦,为什么不在编译器开洞,认为{x, x, x} 就是 std::initializer_list<T> 呢?

// 因为这玩意的确还经常不是initializer_list
struct MyStruct {int a = 1; int b = 1;
};
MyStruct ms = {1, 2};class MyClass {
public:MyClass(int a, int b);// ...
}
MyClass mc {1, 2};

该怎么做

那么如果一定要在 emplace_back 时使用 initializer_list 来简化操作呢?

// 手动构造 initializer_list
a.emplace_back(std::initializer_list<int>{1, 2});// 放弃emplace_back的自动推导,手动指定emplace_back的模板参数类型
// 函数的参数是initializer_list,{1,2}就会触发变身
a.emplace_back<std::initializer_list<int>>({1, 2});// 先用得到 initializer_list, 再传入emplace_back
auto x1 = { 1,2,3,4,5,6 }; // 将花括号初始化器列表推导为std::initializer_list
// auto x2 { 1,2,3,4,5,6 }; // 无法推导
a.emplace_back(x1);

http://www.ppmy.cn/ops/162666.html

相关文章

C语言入门资料分享源码+PDF速查手册

01 目标&#xff1a;掌握基础语法&#xff0c;能编写简单的程序 源码PDF获取 通过网盘分享的文件&#xff1a;C语言入门到精通.rar 链接: https://pan.baidu.com/s/1lcKj3aywRJUecLmoDeQfFg?pwdxiyx 提取码: xiyx 02 环境搭建 安装编译器&#xff08;推荐GCC/MinGW/M…

【Linux第二弹】Linux基础指令(中)

目录 1.cat补充 2.echo指令(含使用) 3.more指令 (用于查看特大文件内容) 4.less指令 (用于查看特大文件内容) 5.head指令 5.1head使用实例 6.tail指令 6.1tail使用实例 7.管道指令( | ) (含使用) 8.date指令 8.1 date使用实例 9.cal指令 9.1 cal使用实例 10.完…

腾讯云扩容记录

腾讯云扩容&#xff1a; sudo yum install -y cloud-utils-growpart 安装扩容工具 sudo file -s /dev/vda1 有数据 sudo LC_ALLen_US.UTF-8 growpart /dev/vda 1 sudo resize2fs /dev/vda1 df -Th 完毕 以下是对执行的命令的详细解释以及背后的原理&#xff1a; 1. 安装 cloud…

【音视频】 H264 H265

概述 项目中接触到一些音视频领域的技术&#xff0c;主要对自己接触到的技术&#xff0c;结合自己的学习内容&#xff0c;进行阶段性总结&#xff0c;如有不正确的地方恳请指正 安防领域摄像头的编码格式目前主流的是H265&#xff0c;但是也存在H264的视频流。项目中经常需要…

Python入门 — 数据存储

可以使用模块 json 来存储数据。一般在程序中&#xff0c;程序都会把用户提供的信息存储在列表和字典等数据结构中&#xff0c;但是当程序关闭时&#xff0c;我们需要保存用户的数据&#xff0c;这时就需要用到模块 json &#xff0c;可以比较方便的存储数据。 模块 json 让你能…

electron多进程通信

进程间通信 | Electron 进程间通信 (IPC) 是在 Electron 中构建功能丰富的桌面应用程序的关键部分之一。 由于主进程和渲染器进程在 Electron 的进程模型具有不同的职责&#xff0c;因此 IPC 是执行许多常见任务的唯一方法&#xff0c;例如从 UI 调用原生 API 或从原生菜单触发…

taoCMS v3.0.2 任意文件读取漏洞(CVE-2022-23316)

漏洞简介&#xff1a; taoCMS v3.0.2 存在任意文件读取漏洞 漏洞环境&#xff1a; 春秋云镜中的漏洞靶标&#xff0c;CVE编号为CVE-2022-23316 漏洞复现 漏洞的位置在 \taocms\include\Model\File.php 中的第 55 行&#xff0c;我们可以看到 path 参数直接传递给file_get_…

wav格式的音频压缩,WAV 转 MP3 VBR 体积缩减比为 13.5%、多个 MP3 格式音频合并为一个、文件夹存在则删除重建,不存在则直接建立

&#x1f947; 版权: 本文由【墨理学AI】原创首发、各位读者大大、敬请查阅、感谢三连 &#x1f389; 声明: 作为全网 AI 领域 干货最多的博主之一&#xff0c;❤️ 不负光阴不负卿 ❤️ 文章目录 问题一&#xff1a;wav格式的音频压缩为哪些格式&#xff0c;网络传输给用户播放…