首页
学习
活动
专区
圈层
工具
发布
50 篇文章
1
Vibe Coding这一年:从“代码苦力”到“超级个体”,我如何把3天的工作压缩进2小时?
2
小程序项目架构设计与基础页面搭建(基础)
3
微信小程序送补贴!手把手教你薅免费云开发资源+混元Token(附使用教程)
4
如何创建一个有效的阅读清单?
5
踩坑记:Elasticsearch 索引写不进去了?可能是触碰了这个隐藏限制
6
RoLID-11K:面向小目标检测的行车记录仪路边垃圾数据集
7
mysql报错通用排查方法 排查MY-001312 can't return a result set in the given context
8
安装并使用谷歌AI编程工具Antigravity(亲测有效)
9
解密Prompt系列68. 告别逐词蹦字 - Transformer 的新推理范式
10
技术人的人生战略:在代码与成长中寻找平衡
11
JavaScript 文件分析与漏洞挖掘指南
12
多 Agent 视角下的自动驾驶系统设计:车端 Agent 与 RSU Agent 协同机制解析
13
构建AI智能体:潜藏秩序的发现:隐因子视角下的SVD推荐知识提取与机理阐释
14
告别浏览器!用Rust打造一键JSON处理神器
15
仅需1元,基于 LangChain 和腾讯混元大模型,实现知识图谱
16
轻量高效!用Docker运行Gogs,搭建属于你的私有GitHub
17
构建AI智能体:SVD知识整理与降维:从数据混沌到语义秩序的智能转换
18
2025年CodeBuddy是如何拯救职场危机中的我?
19
轻量化知识库方案:Docker部署Dokuwiki 的最佳实践
20
踩坑实录:别被 extended_bounds 骗了!ES 直方图聚合的边界陷阱
21
步履不停,共鸣常在:我的 2025 技术旅程与回响
22
构建AI智能体:从SVD的理论到LoRA的实践:大模型低秩微调的内在逻辑
23
[MYSQL] 恢复被drop/truncate的表
24
Sugo Protector 代码保护效果分析报告
25
前端平台大仓应用稳定性治理之路|得物技术
26
C++的5种高级初始化技术:从reserve到piecewise_construct等
27
HierLight-YOLO:面向无人机航拍的层次化轻量目标检测网络
28
金融服务领域的智能体革命:AI智能体解决方案、产业分析与技术实施的战略分析
29
大模型提示词-新手篇
30
2025,一个普通开发者的社区成长地图
31
“氛围编程”正让创意本身成为最终技能
32
AD域攻防权威指南:九.利用备份组获取域Hash
33
【跟着AI学】H5射击游戏开发实录:射击游戏
34
这一年,熬过许过夜,也有些许收获 | 2025年终总结
35
2025,一个技术徘徊者的AI工具真实答卷
36
告别手撸架构图!AI+Ooder实现漂亮架构+动态交互+全栈可视化实战指南
37
GitHub 霸榜:让你的 Claude 拥有“设计总监”级的品味,只要一行命令
38
构建AI智能体:AI古典文学:基于LoRA微调本地大模型打造唐诗生成器
39
拥抱人机共生,锻造不可替代的“金头脑”
40
[MYSQL] 5.7能否从ibdata1中提取出表DDL
41
Spring Boot 实战:手把手教你实现腾讯云 COS 对象存储文件上传
42
解密Prompt系列67. 智能体的经济学:从架构选型到工具预算
43
Google OCS光路解耦揭秘:寒武纪大爆发,从供应链双轨到CPO百万卡全光计算织物
44
未来已来 | 写给 .NET 开发者的 2025 年度总结
45
MYSQL实战:深入理解内存临时表优化
46
Ooder框架规范执行计划:企业级AI实施流程与大模型协作指南
47
openGauss 核心体系架构深度解析
48
架构视角:Jackson3新特性
49
LLM架构机制管窥:作为黑板的上下文窗口
50
LiveKit Agents 深度技术架构剖析
清单首页123文章详情

C++的5种高级初始化技术:从reserve到piecewise_construct等

{"type":"doc","content":[{"type":"heading","attrs":{"id":"ff780eaa-133a-4578-9f71-8dece0bba419","textAlign":"inherit","indent":0,"level":1,"isHoverDragHandle":false},"content":[{"type":"text","text":"一、简介"}]},{"type":"paragraph","attrs":{"id":"3a3a855c-9d00-4bad-90bc-223938c63337","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"从动态容器操作到编译时常量,C++提供了多种技术。在这篇文章中,将深入研究高级初始化方法,如"},{"type":"text","marks":[{"type":"code"}],"text":"reserve()"},{"type":"text","text":"和"},{"type":"text","marks":[{"type":"code"}],"text":"emplace_back"},{"type":"text","text":",以及使用 "},{"type":"text","marks":[{"type":"code"}],"text":"piecewise_construct"},{"type":"text","text":" 和 "},{"type":"text","marks":[{"type":"code"}],"text":"forward_as_tuple"},{"type":"text","text":" 构建元组。由于这些技术,我们可以减少临时对象的数量,并更有效地创建变量。"}]},{"type":"paragraph","attrs":{"id":"a25faefa-f6b9-434b-8c6f-8e38f8d4ba33","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"在讲解高级初始化技术之前,先看一个前置背景,使用下面的类来方便地说明何时调用它的特殊成员函数。这样,我们就能看到额外的临时对象。"}]},{"type":"codeBlock","attrs":{"id":"eed18c2c-e3e7-4df6-a34b-542dadcd051e","language":"cpp","theme":"atom-one-dark","runtimes":0,"isHoverDragHandle":false,"key":"","languageByAi":"cpp"},"content":[{"type":"text","text":"struct MyType {\n MyType() { std::cout << \"MyType default\\n\"; }\n explicit MyType(std::string str) : str_(std::move(str)) { \n std::cout << std::format(\"MyType {}\\n\", str_); \n }\n ~MyType() { \n std::cout << std::format(\"~MyType {}\\n\", str_); \n }\n MyType(const MyType& other) : str_(other.str_) { \n std::cout << std::format(\"MyType copy {}\\n\", str_); \n }\n MyType(MyType&& other) noexcept : str_(std::move(other.str_)) { \n std::cout << std::format(\"MyType move {}\\n\", str_); \n }\n MyType& operator=(const MyType& other) { \n if (this != &other)\n str_ = other.str_;\n std::cout << std::format(\"MyType = {}\\n\", str_); \n return *this;\n }\n MyType& operator=(MyType&& other) noexcept { \n if (this != &other)\n str_ = std::move(other.str_);\n std::cout << std::format(\"MyType = move {}\\n\", str_); \n return *this; \n }\n std::string str_;\n};"}]},{"type":"paragraph","attrs":{"id":"84983ab3-271b-4663-80ec-0a5e21aef00e","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"现在可以从相对简单但基本的元素开始。"}]},{"type":"heading","attrs":{"id":"8ee95cd4-5575-40b3-b5d7-e58a5426cfd1","textAlign":"inherit","indent":0,"level":1,"isHoverDragHandle":false},"content":[{"type":"text","text":"二、reserve 结合 emplace_back"}]},{"type":"paragraph","attrs":{"id":"bb8ba82f-bff8-4b66-91c0-b11481893d22","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"C++中的"},{"type":"text","marks":[{"type":"code"}],"text":"vector"},{"type":"text","text":"是一种可以根据需要增长的动态数组。但是,每次向量增长超过其当前容量时,它可能需要重新分配内存,这代价是昂贵的。为了优化这一点,可以使用"},{"type":"text","marks":[{"type":"code"}],"text":"reserve()"},{"type":"text","text":"方法结合"},{"type":"text","marks":[{"type":"code"}],"text":"emplace_back()"},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"id":"0710aa1a-e531-454d-a4e1-31d22cb43bfb","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"code"}],"text":"reserve"},{"type":"text","text":"方法不改变"},{"type":"text","marks":[{"type":"code"}],"text":"vector"},{"type":"text","text":"的大小,但确保"},{"type":"text","marks":[{"type":"code"}],"text":"vector"},{"type":"text","text":"有足够的已分配内存来存储指定数量的元素。通过提前预留空间,可以防止在向向量添加元素时进行多次重新分配。"}]},{"type":"paragraph","attrs":{"id":"53ebd9d2-d979-4402-a73b-51f1ccf3d048","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"示例:"}]},{"type":"codeBlock","attrs":{"id":"42af33a5-4651-45f3-a931-226c7abfb28b","language":"cpp","theme":"atom-one-dark","runtimes":0,"isHoverDragHandle":false,"key":"","languageByAi":"cpp"},"content":[{"type":"text","text":"#include <iostream>\n#include <string>\n#include <vector>\n#include <format>\n\nstruct MyType {\n MyType() { std::cout << \"MyType default\\n\"; }\n explicit MyType(std::string str) : str_(std::move(str)) { \n std::cout << std::format(\"MyType {}\\n\", str_); \n }\n ~MyType() { \n std::cout << std::format(\"~MyType {}\\n\", str_); \n }\n MyType(const MyType& other) : str_(other.str_) { \n std::cout << std::format(\"MyType copy {}\\n\", str_); \n }\n MyType(MyType&& other) noexcept : str_(std::move(other.str_)) { \n std::cout << std::format(\"MyType move {}\\n\", str_); \n }\n MyType& operator=(const MyType& other) { \n if (this != &other)\n str_ = other.str_;\n std::cout << std::format(\"MyType = {}\\n\", str_); \n return *this;\n }\n MyType& operator=(MyType&& other) noexcept { \n if (this != &other)\n str_ = std::move(other.str_);\n std::cout << std::format(\"MyType = move {}\\n\", str_); \n return *this; \n }\n std::string str_;\n};\n\nint main() { \n {\n std::cout << \"--- push_back\\n\";\n std::vector<MyType> vec;\n vec.push_back(MyType(\"First\"));\n std::cout << std::format(\"capacity: {}\\n\", vec.capacity());\n vec.push_back(MyType(\"Second\"));\n }\n {\n std::cout << \"--- emplace_back\\n\";\n std::vector<MyType> vec;\n vec.emplace_back(\"First\");\n std::cout << std::format(\"capacity: {}\\n\", vec.capacity());\n vec.emplace_back(\"Second\");\n }\n {\n std::cout << \"--- reserve() + emplace_\\n\";\n std::vector<MyType> vec;\n vec.reserve(2); // Reserve space for 2 elements\n vec.emplace_back(\"First\");\n vec.emplace_back(\"Second\");\n }\n}\n"}]},{"type":"paragraph","attrs":{"id":"253dabf6-f193-4926-836a-2109796c9b0e","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"输出:"}]},{"type":"codeBlock","attrs":{"id":"628599ff-c440-455a-8bf3-8a72339fea19","language":"javascript","theme":"atom-one-dark","runtimes":0,"isHoverDragHandle":false,"key":"","languageByAi":"javascript"},"content":[{"type":"text","text":"--- push_back\nMyType First\nMyType move First\n~MyType \ncapacity: 1\nMyType Second\nMyType move Second\nMyType move First\n~MyType \n~MyType \n~MyType First\n~MyType Second\n--- emplace_back\nMyType First\ncapacity: 1\nMyType Second\nMyType move First\n~MyType \n~MyType First\n~MyType Second\n--- reserve() + emplace_\nMyType First\nMyType Second\n~MyType First\n~MyType Second"}]},{"type":"paragraph","attrs":{"id":"bda69ce9-7fcb-4e77-98f0-ce195ebc0155","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"在这个例子中,可以看到三种插入技术之间的比较:"}]},{"type":"bulletList","attrs":{"id":"78b6cd64-4c0c-47c3-9960-946b990ca18f","isHoverDragHandle":false},"content":[{"type":"listItem","attrs":{"id":"5f4a2301-dd87-4985-a1f4-99ed3c76a60d"},"content":[{"type":"paragraph","attrs":{"id":"6512412b-3fc2-4dd6-9206-42f0a53f7d65","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"code"}],"text":"push_back()"},{"type":"text","text":"。"}]}]},{"type":"listItem","attrs":{"id":"664fdc1a-d4f8-4284-aabe-71f49fa375b9"},"content":[{"type":"paragraph","attrs":{"id":"2ef5312e-3bfd-41cc-921e-4f77920a4bb4","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"code"}],"text":"emplace_back()"},{"type":"text","marks":[{"type":"textStyle","attrs":{"color":"","background":""}}],"text":"。"}]}]},{"type":"listItem","attrs":{"id":"1e7eb712-b687-4e44-bac5-f720635fc45e"},"content":[{"type":"paragraph","attrs":{"id":"8b4320bb-67e1-4f88-a2c5-f30aef7b3b5b","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"code"}],"text":"reserve()"},{"type":"text","marks":[{"type":"textStyle","attrs":{"color":"","background":""}}],"text":"结合"},{"type":"text","marks":[{"type":"code"}],"text":"emplace_back"},{"type":"text","marks":[{"type":"textStyle","attrs":{"color":"","background":""}}],"text":"。"}]}]}]},{"type":"paragraph","attrs":{"id":"f73073ce-6438-4864-8af2-479483a5082b","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"第一种情况下,必须将临时对象传递给"},{"type":"text","marks":[{"type":"code"}],"text":"push_back"},{"type":"text","text":",并移动它们来初始化"},{"type":"text","marks":[{"type":"code"}],"text":"vector"},{"type":"text","text":"的元素。但是还有一个重新分配,因为当添加第二个元素时,"},{"type":"text","marks":[{"type":"code"}],"text":"vector"},{"type":"text","text":"必须增长。"}]},{"type":"paragraph","attrs":{"id":"2e8d8255-1968-45c9-a982-38e504c682b4","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"相对而言,"},{"type":"text","marks":[{"type":"code"}],"text":"emplace_back()"},{"type":"text","text":"技术更好,更容易编写,因为没有创建临时对象。"}]},{"type":"paragraph","attrs":{"id":"a14c43dd-13f0-4ccc-94ec-0688c1338008","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"但是,第三种选择是最有效的,因为可以预先预留空间,然后在适当的地方创建元素。"}]},{"type":"paragraph","attrs":{"id":"a08c5c1c-984d-4f20-86d0-d32faf8abf9a","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"通过使用"},{"type":"text","marks":[{"type":"code"}],"text":"reserve"},{"type":"text","text":"和"},{"type":"text","marks":[{"type":"code"}],"text":"emplace_back"},{"type":"text","text":",可以确保"},{"type":"text","marks":[{"type":"code"}],"text":"vector"},{"type":"text","text":"在添加元素到预留容量时不需要重新分配内存。这种组合是优化性能的一种强大方式,特别是在向vector`中添加多个元素时。"}]},{"type":"heading","attrs":{"id":"954a7d01-46e4-419b-8588-2d3ea3766630","textAlign":"inherit","indent":0,"level":1,"isHoverDragHandle":false},"content":[{"type":"text","text":"三、C++ 20的constinit"}]},{"type":"paragraph","attrs":{"id":"54b7a467-d967-484c-b9d7-7e6289e9d5fc","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"code"}],"text":"constinit"},{"type":"text","text":"是一个强大的工具,用于强制常量初始化,特别是对于静态或线程局部变量。这个关键字在C++ 20中引入,它解决了C++中一个长期存在的难题:静态初始化顺序的问题。通过确保变量在编译时初始化,"},{"type":"text","marks":[{"type":"code"}],"text":"constinit"},{"type":"text","text":"提供了一个更可预测和更安全的初始化过程。"}]},{"type":"paragraph","attrs":{"id":"f006081c-6f3e-4c28-b49a-be0558c7a086","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"在其核心,"},{"type":"text","marks":[{"type":"code"}],"text":"constinit"},{"type":"text","text":"保证它所限定的变量在编译时被初始化。这对全局变量或静态变量尤其有益,可以确保它们不受动态初始化顺序问题的影响。"}]},{"type":"paragraph","attrs":{"id":"15118983-692d-4675-98ba-5232ec07b54a","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"示例:"}]},{"type":"codeBlock","attrs":{"id":"ddf61fa5-b467-439c-ba86-75eefae12bac","language":"cpp","theme":"atom-one-dark","runtimes":0,"isHoverDragHandle":false,"key":"","languageByAi":"cpp"},"content":[{"type":"text","text":"#include <array>\n\n// 编译期初始化\nconstexpr int compute(int v) { return v*v*v; }\nconstinit int global = compute(10);\n\n// 这个将不再起作用:\n// constinit int another = global;\n\nint main() {\n // 但允许以后更改……\n global = 100;\n\n // global is not constant!\n // std::array<int, global> arr;\n}"}]},{"type":"paragraph","attrs":{"id":"73e8f07c-c770-4beb-9651-6c28bee0a6db","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"在上面的代码中,全局变量是在编译时使用"},{"type":"text","marks":[{"type":"code"}],"text":"compute"},{"type":"text","text":"函数初始化的。然而,与"},{"type":"text","marks":[{"type":"code"}],"text":"const"},{"type":"text","text":"或"},{"type":"text","marks":[{"type":"code"}],"text":"constexpr"},{"type":"text","text":"不同,"},{"type":"text","marks":[{"type":"code"}],"text":"constinit"},{"type":"text","text":"不会使变量不可变。也就是说,虽然它的初始值是在编译时设置的,但可以在运行时对其进行修改,如"},{"type":"text","marks":[{"type":"code"}],"text":"main"},{"type":"text","text":"函数所示。此外,由于"},{"type":"text","marks":[{"type":"code"}],"text":"constinit"},{"type":"text","text":"变量不是"},{"type":"text","marks":[{"type":"code"}],"text":"constexpr"},{"type":"text","text":",因此不能使用它初始化另一个"},{"type":"text","marks":[{"type":"code"}],"text":"constinit"},{"type":"text","text":"对象(如其他的"},{"type":"text","marks":[{"type":"code"}],"text":"int"},{"type":"text","text":")。"}]},{"type":"heading","attrs":{"id":"99e08e2d-36d2-4201-ab53-a6b1fd8a9d06","textAlign":"inherit","indent":0,"level":1,"isHoverDragHandle":false},"content":[{"type":"text","text":"四、Lambda表达式和初始化"}]},{"type":"paragraph","attrs":{"id":"36aa1e58-b1b3-4122-93a9-edf25e47286d","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"C++ 14对Lambda捕获进行了重大更新,引入了在Lambda捕获子句中直接初始化新数据成员的能力。这个特性称为带有初始化器的捕获或广义Lambda捕获,它在使用Lambda时为我们提供了更大的灵活性和精度。"}]},{"type":"paragraph","attrs":{"id":"8b07399c-7bca-4a19-852e-6d6ee4eeafe5","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"传统上,Lambda表达式可以从其封闭范围捕获变量。在C++ 14中,现在可以直接在捕获子句中创建和初始化新的数据成员,使Lambdas更加通用。"}]},{"type":"paragraph","attrs":{"id":"2b06605e-d641-440d-9b12-167211917348","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"示例:"}]},{"type":"codeBlock","attrs":{"id":"092f4773-49fd-4444-884d-d25c91e792b2","language":"cpp","theme":"atom-one-dark","runtimes":0,"isHoverDragHandle":false,"key":"","languageByAi":"cpp"},"content":[{"type":"text","text":"#include <iostream>\n\nint main() {\n int x = 30;\n int y = 12;\n const auto foo = [z = x + y]() { \n \tstd::cout << z; \n };\n x = 0;\n y = 0;\n foo();\n}"}]},{"type":"paragraph","attrs":{"id":"684ce2f8-c1e2-4aa3-8ee1-3592febff6d6","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"输出:"}]},{"type":"codeBlock","attrs":{"id":"de2d75fe-b324-42fd-9524-1b5ca8395b54","language":"javascript","theme":"atom-one-dark","runtimes":0,"isHoverDragHandle":false,"key":"","languageByAi":"javascript"},"content":[{"type":"text","text":"42"}]},{"type":"paragraph","attrs":{"id":"f4d4c5cd-2901-4288-97b5-1b4cacdec6e3","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"在这里,创建了一个新的数据成员"},{"type":"text","marks":[{"type":"code"}],"text":"z"},{"type":"text","text":",并用"},{"type":"text","marks":[{"type":"code"}],"text":"x"},{"type":"text","text":"和"},{"type":"text","marks":[{"type":"code"}],"text":"y"},{"type":"text","text":"的和进行初始化。这个初始化发生在Lambda定义点,而不是调用点。因此,即使在Lambda定义之后修改了"},{"type":"text","marks":[{"type":"code"}],"text":"x"},{"type":"text","text":"和"},{"type":"text","marks":[{"type":"code"}],"text":"y"},{"type":"text","text":", "},{"type":"text","marks":[{"type":"code"}],"text":"z"},{"type":"text","text":"仍然保持其初始值。"}]},{"type":"paragraph","attrs":{"id":"df17164f-a642-48c8-96fe-ba5a2fdb3ce6","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"为了更好地理解这个特性,来看看Lambda是如何转换为可调用类型的:"}]},{"type":"codeBlock","attrs":{"id":"59fdf229-2393-483a-ba09-a80bb3938a9a","language":"cpp","theme":"atom-one-dark","runtimes":0,"isHoverDragHandle":false,"key":"","languageByAi":"cpp"},"content":[{"type":"text","text":"struct _unnamedLambda {\n void operator()() const {\n std::cout << z;\n }\n int z;\n} someInstance;"}]},{"type":"paragraph","attrs":{"id":"008cc1c2-ae70-4fd5-b54e-2295c3488eb7","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"Lambda本质上变成了一个带有"},{"type":"text","marks":[{"type":"code"}],"text":"operator()()"},{"type":"text","text":"方法和数据成员"},{"type":"text","marks":[{"type":"code"}],"text":"z"},{"type":"text","text":"的未命名结构的实例。"}]},{"type":"paragraph","attrs":{"id":"3119f35b-9943-4e56-ac9f-126f570423eb","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"使用初始化器捕获不仅限于简单类型,还可以捕获引用。"}]},{"type":"paragraph","attrs":{"id":"c290027a-0d94-463b-b1f2-09ddcc7df7e9","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"这种技术在什么情况会很方便呢?至少有两种情况:"}]},{"type":"bulletList","attrs":{"id":"8b8e7851-5d74-4793-a9d8-4c4ead61bf0a","isHoverDragHandle":false},"content":[{"type":"listItem","attrs":{"id":"54c84543-e457-421d-8422-eb20503f7819"},"content":[{"type":"paragraph","attrs":{"id":"74b30697-8146-443b-b291-47b709ea1a66","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"捕获只能移动的类型,特别是通过值。"}]}]},{"type":"listItem","attrs":{"id":"67e76894-e524-4b07-a48f-249120481c87"},"content":[{"type":"paragraph","attrs":{"id":"8f06a89e-0592-4c90-b719-589b611b1b62","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"优化。"}]}]}]},{"type":"paragraph","attrs":{"id":"e9a39958-fef1-454e-89d5-163d8eb6fb75","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"考虑第一种情况,下面是捕获"},{"type":"text","marks":[{"type":"code"}],"text":"std::unique_ptr"},{"type":"text","text":"的方法:"}]},{"type":"codeBlock","attrs":{"id":"da7118db-6b7c-4ee9-87cc-d21de9e84703","language":"cpp","theme":"atom-one-dark","runtimes":0,"isHoverDragHandle":false,"key":"","languageByAi":"cpp"},"content":[{"type":"text","text":"#include <iostream>\n#include <memory>\n\nint main(){\n std::unique_ptr<int> p(new int{10});\n const auto bar = [ptr=std::move(p)] {\n std::cout << \"pointer in lambda: \" << ptr.get() << '\\n';\n };\n std::cout << \"pointer in main(): \" << p.get() << '\\n';\n bar();\n}"}]},{"type":"paragraph","attrs":{"id":"eba7f2b6-28c3-41e3-8e3c-cdf65cb5e86b","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"在以前的C++ 11中,不能按值捕获惟一的指针,只可能通过引用捕获。现在,从C++ 14开始,可以将对象移动到闭包类型的成员中。"}]},{"type":"paragraph","attrs":{"id":"a18f8de3-9fdc-4e54-82b7-25add5481191","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"另一个情况是优化。比如,如果捕获一个变量,然后计算一些临时对象:"}]},{"type":"codeBlock","attrs":{"id":"57c9b22a-53cd-4210-90a6-37da38d53c3b","language":"cpp","theme":"atom-one-dark","runtimes":0,"isHoverDragHandle":false,"key":"","languageByAi":"cpp"},"content":[{"type":"text","text":"auto result = std::find_if(vs.begin(), vs.end(),\n [&prefix](const std::string& s) {\n return s == prefix + \"bars\"; \n }\n );"}]},{"type":"paragraph","attrs":{"id":"7748bc87-d113-4603-804b-16da0330d484","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"为什么不计算一次并将其存储在lambda对象中呢?"}]},{"type":"codeBlock","attrs":{"id":"fed4433c-dad2-4bba-a618-1c98a80efe0b","language":"cpp","theme":"atom-one-dark","runtimes":0,"isHoverDragHandle":false,"key":"","languageByAi":"cpp"},"content":[{"type":"text","text":"result = std::find_if(vs.begin(), vs.end(), \n [savedString = prefix + \"bars\"](const std::string& s) { \n return s == savedString; \n }\n );"}]},{"type":"paragraph","attrs":{"id":"3bda3376-93c5-413b-a343-9f31df0f9098","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"这样,"},{"type":"text","marks":[{"type":"code"}],"text":"savedString"},{"type":"text","text":"只计算一次,而不是每次调用函数时都计算一次。"}]},{"type":"heading","attrs":{"id":"4acdd6af-1c07-478b-be2a-72aaefb94cd4","textAlign":"inherit","indent":0,"level":1,"isHoverDragHandle":false},"content":[{"type":"text","text":"五、make_unique_for_overwrite"}]},{"type":"paragraph","attrs":{"id":"dceaf816-18ff-42ab-81b8-d715e9fb365c","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"通过使用智能指针,获得了能够显著降低与动态内存分配相关的风险的工具。但是,和所有任何工具一样,总是有改进和优化的空间。"}]},{"type":"paragraph","attrs":{"id":"1227eb83-fb19-4bda-aacf-9862af08bec2","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"当使用"},{"type":"text","marks":[{"type":"code"}],"text":"make_unique"},{"type":"text","text":"(或"},{"type":"text","marks":[{"type":"code"}],"text":"make_shared"},{"type":"text","text":")分配数组时,默认行为是对每个元素进行值初始化。这意味着对于内置类型,每个元素都设置为零,对于自定义类型,调用其默认构造函数。虽然这确保了内存被初始化为已知状态,但它带来了性能开销,特别是当想要立即覆盖已分配的内存时。"}]},{"type":"paragraph","attrs":{"id":"b857dd06-623d-4c50-a9f7-179ec038a54d","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"示例:"}]},{"type":"codeBlock","attrs":{"id":"539129db-0c97-4cf6-a3f8-22fc8afcff5f","language":"cpp","theme":"atom-one-dark","runtimes":0,"isHoverDragHandle":false,"key":"","languageByAi":"cpp"},"content":[{"type":"text","text":"auto ptr = std::make_unique<int[]>(1000); "}]},{"type":"paragraph","attrs":{"id":"0511fa10-f0c7-4605-9602-767b2bb8afc1","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"这一行不仅为1000个整数分配内存,而且还将每个整数初始化为零。如果下一步是用来自文件或网络操作的数据填充该内存,那么初始归零是不必要的,也是浪费的。"}]},{"type":"paragraph","attrs":{"id":"4b806060-aa7d-4c94-bd13-a7f8dfcbb9bf","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"为了解决这种低效率问题,C++ 20引入了"},{"type":"text","marks":[{"type":"code"}],"text":"make_unique_for_overwrite"},{"type":"text","text":"和"},{"type":"text","marks":[{"type":"code"}],"text":"make_shared_for_overwrite"},{"type":"text","text":"。这些函数分配内存时不需要对其进行值初始化,这使得它们在直接打算覆盖内存时更快。"}]},{"type":"codeBlock","attrs":{"id":"4fdf18cc-bc89-4aea-b190-36e6cb209ee4","language":"cpp","theme":"atom-one-dark","runtimes":0,"isHoverDragHandle":false,"key":"","languageByAi":"cpp"},"content":[{"type":"text","text":"auto ptr = std::make_unique_for_overwrite<int[]>(1000);"}]},{"type":"paragraph","attrs":{"id":"50a619e0-debc-4ae9-a805-5d625a9885ca","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"当分配的内存立即被其他数据覆盖时,"},{"type":"text","marks":[{"type":"code"}],"text":"*_for_overwrite"},{"type":"text","text":"函数是最有用的。但是要注意,如果内存没有被覆盖,它包含不确定的值,这时如果访问的话可能导致未定义的行为。"}]},{"type":"paragraph","attrs":{"id":"c5ddbf11-4603-4e49-b017-04267f9b0c77","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"这些新功能可以显著提高执行大量内存操作的应用程序的性能,例如数据处理工具或游戏引擎。"}]},{"type":"heading","attrs":{"id":"3e7df273-b0df-46c8-82d8-c6a7aff1d1ef","textAlign":"inherit","indent":0,"level":1,"isHoverDragHandle":false},"content":[{"type":"text","text":"六、piecewise_construct 和 forward_as_tuple"}]},{"type":"paragraph","attrs":{"id":"fd37671b-4888-4f05-850a-37ce8358d50b","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"最后,让我们看看第五种技术:使用多参数构造函数直接初始化对或元组。这就是"},{"type":"text","marks":[{"type":"code"}],"text":"std::piecewise_construct"},{"type":"text","text":"和"},{"type":"text","marks":[{"type":"code"}],"text":"std::forward_as_tuple"},{"type":"text","text":"发挥作用的地方。"}]},{"type":"paragraph","attrs":{"id":"f9f8958e-2a90-43ab-af56-4a8950880a1f","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"示例:"}]},{"type":"codeBlock","attrs":{"id":"0f2357cf-25b9-4f46-aa93-753d0909000a","language":"cpp","theme":"atom-one-dark","runtimes":0,"isHoverDragHandle":false,"key":"","languageByAi":"cpp"},"content":[{"type":"text","text":"std::pair<MyType, MyType> p { \"one\", \"two\" };"}]},{"type":"paragraph","attrs":{"id":"7ec11e10-0e03-4f9d-b275-2ee5eda8db59","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"上面的代码创建了没有额外临时"},{"type":"text","marks":[{"type":"code"}],"text":"MyType"},{"type":"text","text":"对象的"},{"type":"text","marks":[{"type":"code"}],"text":"pair"},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"id":"3d7af7a8-f6c6-41cf-a4d9-01e5d36e914a","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"但是,如果有一个额外的构造函数接受两个参数,那该怎么办呢?"}]},{"type":"codeBlock","attrs":{"id":"a7472ce9-328c-4130-9caa-d609bd3a97cc","language":"cpp","theme":"atom-one-dark","runtimes":0,"isHoverDragHandle":false,"key":"","languageByAi":"cpp"},"content":[{"type":"text","text":"MyType(std::string str, int a)"}]},{"type":"paragraph","attrs":{"id":"5ba69fcd-3d0c-4b6c-bcb5-99ab2e9a3264","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"在这种情况下,如果这样:"}]},{"type":"codeBlock","attrs":{"id":"970a2a6f-896c-460b-94e1-47e67224cfa6","language":"cpp","theme":"atom-one-dark","runtimes":0,"isHoverDragHandle":false,"key":"","languageByAi":"cpp"},"content":[{"type":"text","text":"std::pair<MyType, MyType> p { \"one\", 1, \"two\", 2 };"}]},{"type":"paragraph","attrs":{"id":"fc3e95b4-bf86-4145-acd2-b161c70503fd","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"毫无疑问,它失败了,因为该调用对编译器是二义性的。"}]},{"type":"paragraph","attrs":{"id":"2d154404-8444-41cb-b081-2995d1d30b97","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"在这些情况下,"},{"type":"text","marks":[{"type":"code"}],"text":"std::piecewise_construct"},{"type":"text","text":"可以提供帮助。它是一个指示"},{"type":"text","marks":[{"type":"code"}],"text":"std::pair"},{"type":"text","text":"执行分段构造的标记。当与"},{"type":"text","marks":[{"type":"code"}],"text":"std::forward_as_tuple"},{"type":"text","text":"(创建左值或右值引用的元组)结合使用时,可以将多个参数转发给"},{"type":"text","marks":[{"type":"code"}],"text":"pair"},{"type":"text","text":"元素的构造函数。"}]},{"type":"codeBlock","attrs":{"id":"c703abcd-7ff4-4cde-b633-cc70868048ba","language":"cpp","theme":"atom-one-dark","runtimes":0,"isHoverDragHandle":false,"key":"","languageByAi":"cpp"},"content":[{"type":"text","text":"#include <iostream>\n#include <string>\n#include <format>\n\nstruct MyType {\n MyType() { std::cout << \"MyType default\\n\"; }\n explicit MyType(std::string str) : str_(std::move(str)) { \n std::cout << std::format(\"MyType {}\\n\", str_); \n }\n MyType(std::string str, int a) : str_(std::move(str)) { \n std::cout << std::format(\"MyType {}, {}\\n\", str_, a); \n }\n ~MyType() { \n std::cout << std::format(\"~MyType {}\\n\", str_); \n }\n MyType(const MyType& other) : str_(other.str_) { \n std::cout << std::format(\"MyType copy {}\\n\", str_); \n }\n MyType(MyType&& other) noexcept : str_(std::move(other.str_)) { \n std::cout << std::format(\"MyType move {}\\n\", str_); \n }\n MyType& operator=(const MyType& other) { \n if (this != &other)\n str_ = other.str_;\n std::cout << std::format(\"MyType = {}\\n\", str_); \n return *this;\n }\n MyType& operator=(MyType&& other) noexcept { \n if (this != &other)\n str_ = std::move(other.str_);\n std::cout << std::format(\"MyType = move {}\\n\", str_); \n return *this; \n }\n std::string str_;\n};\n\nint main() {\n {\n std::cout << \"regular: \\n\";\n std::pair<MyType, MyType> p { MyType{\"one\", 1}, MyType{\"two\", 2}};\n }\n {\n std::cout << \"piecewise + forward: \\n\";\n std::pair<MyType, MyType>p2(std::piecewise_construct,\n std::forward_as_tuple(\"one\", 1),\n std::forward_as_tuple(\"two\", 2));\n }\n}\n"}]},{"type":"paragraph","attrs":{"id":"aca6042d-0c75-46af-9b56-ae269bdb1ae0","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"运行这个程序会看到以下输出:"}]},{"type":"codeBlock","attrs":{"id":"aba7728c-4b29-4eb7-8ef2-94fbe7578948","language":"javascript","theme":"atom-one-dark","runtimes":0,"isHoverDragHandle":false,"key":"","languageByAi":"javascript"},"content":[{"type":"text","text":"regular: \nMyType one, 1\nMyType two, 2\nMyType move one\nMyType move two\n~MyType \n~MyType \n~MyType two\n~MyType one\npiecewise + forward: \nMyType one, 1\nMyType two, 2\n~MyType two\n~MyType one"}]},{"type":"paragraph","attrs":{"id":"c485b795-92c3-46d1-88ef-7999bf2794e6","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"可以看到,常规方法创建了两个临时对象。而 使用分段选项可以直接将参数传递给"},{"type":"text","marks":[{"type":"code"}],"text":"pair"},{"type":"text","text":"的元素。"}]},{"type":"paragraph","attrs":{"id":"7f68c7af-a6c1-4669-819b-4e886436fd6f","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"code"}],"text":"std::piecewise_construct"},{"type":"text","text":"对于像"},{"type":"text","marks":[{"type":"code"}],"text":"std::map"},{"type":"text","text":"和"},{"type":"text","marks":[{"type":"code"}],"text":"std::unordered_map"},{"type":"text","text":"这样存储键值对("},{"type":"text","marks":[{"type":"code"}],"text":"std::pair"},{"type":"text","text":")的容器特别有用。当想要向这些容器中插入元素,并且键或值(或两者)具有多参数构造函数或不可复制时,"},{"type":"text","marks":[{"type":"code"}],"text":"std::piecewise_construct"},{"type":"text","text":"的实用程序变得很方便。"}]},{"type":"paragraph","attrs":{"id":"4175b50b-62d3-4a6c-8837-fb754613772b","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"示例:"}]},{"type":"codeBlock","attrs":{"id":"3852a2ef-5007-4782-b331-7bd73600bb2a","language":"cpp","theme":"atom-one-dark","runtimes":0,"isHoverDragHandle":false,"key":"","languageByAi":"cpp"},"content":[{"type":"text","text":"#include <string>\n#include <map>\n\nstruct Key {\n Key(int a, int b) : sum(a + b) {}\n int sum;\n bool operator<(const Key& other) const { \n return sum < other.sum; \n }\n};\n\nstruct Value {\n Value(const std::string& s, double d) : name(s), data(d) {}\n std::string name;\n double data;\n};\n\nint main() {\n std::map<Key, Value> myMap;\n\n // doesn't compile: ambiguous\n // myMap.emplace(3, 4, \"example\", 42.0);\n\n // works:\n myMap.emplace(\n std::piecewise_construct,\n std::forward_as_tuple(3, 4), \n std::forward_as_tuple(\"example\", 42.0) \n );\n}\n\n"}]},{"type":"paragraph","attrs":{"id":"669a541d-0ed5-400e-becc-1aebed9d9dfb","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false}},{"type":"heading","attrs":{"id":"7830176e-5f32-4778-b728-11af89872215","textAlign":"inherit","indent":0,"level":1,"isHoverDragHandle":false},"content":[{"type":"text","text":"七、总结"}]},{"type":"paragraph","attrs":{"id":"6026077b-6b7b-46d8-8468-7ad758348174","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"本文探讨了初始化C++代码的各种技术。深入研究了现代C++特性的复杂性,包括"},{"type":"text","marks":[{"type":"code"}],"text":"reserve"},{"type":"text","text":"和"},{"type":"text","marks":[{"type":"code"}],"text":"emplace_back"},{"type":"text","text":"的效率、"},{"type":"text","marks":[{"type":"code"}],"text":"constinit"},{"type":"text","text":"的准确性和"},{"type":"text","marks":[{"type":"code"}],"text":"lambda"},{"type":"text","text":"初始化的灵活性。此外,还研究了"},{"type":"text","marks":[{"type":"code"}],"text":"piecewise "},{"type":"text","text":"和"},{"type":"text","marks":[{"type":"code"}],"text":"forward_as_tuple"},{"type":"text","text":"的细微差别。这些高级技术展示了c++语言的发展和强大,并为开发人员提供了编写更具表现力、更高效和更通用的代码的能力。"}]},{"type":"paragraph","attrs":{"id":"2a56d0ec-2468-4108-a90f-3937e7f45912","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"有些人可能会认为这是语言中不必要的复杂,这不是一定的。考虑"},{"type":"text","marks":[{"type":"code"}],"text":"emplace()"},{"type":"text","text":"函数,它可以改进容器插入。但是,如果不需要优化,可以使用更简单的代码来传递临时对象。"}]},{"type":"image","attrs":{"id":"a34d97d6-65f6-4155-bb06-d0835b4c57bc","src":"https://developer.qcloudimg.com/http-save/audit-11218869/bfbeb08f12cfcd1237403f4d8a29b04c.png","extension":"png","align":"center","alt":"","showAlt":false,"href":"","boxShadow":"","width":1080,"aspectRatio":"3.600000","status":"success","showText":true,"isPercentage":false,"percentage":0,"isHoverDragHandle":false}},{"type":"paragraph","attrs":{"id":"3de55e1e-8023-48c4-b7cf-a5cf2cca8e5e","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false}}]}

下一篇
举报
领券