C++20 指定初始化器
指定初始化器
对于聚合,C++20 提供了一种方法来指定应该用传递的初始值初始化哪个成员,但只能使用它
来跳过参数。
假设有以下聚合类型:
struct Value
{double amount{0};int precision{2};std::string unit{"Dollar";};
};
那么,现在支持以下方式来初始化该类型的值:
Value v1{100}; // OK (not designated initializers)
Value v2{.amount = 100, .unit = "Euro"}; // OK (second member has default value)
Value v3{.precision = 8, .unit = "$"}; // OK (first member has default value)
完整的示例请参见lang/designated.cpp。
注意以下限制:
- 必须使用= 或{} 传递初始值。
- 可以跳过会员,但必须遵循其顺序。按名称初始化的成员的顺序必须与声明中的顺序匹配。
- 必须对所有参数使用指定初始化式,或者不使用指定初始化式。不允许混合初始化。
- 不支持对数组使用指定初始化。
- 可以使用指定初始化式嵌套初始化,但不能直接使用.mem.mem。
- 初始化带有圆括号的聚合时,不能使用指定初始化器。
- 指定初始化式也可以用在联合体中。
例如:
Value v4{100, .unit = "Euro"}; // ERROR: all or none designated
Value v5{.unit = "$", .amount = 20}; // ERROR: invalid order
Value v6(.amount = 29.9, .unit = "Euro"); // ERROR: only supported for curly braces
#include <iostream>
#include <string>struct Value{double amount = 0.0;int precision = 2;std::string unit = "Dollor";
};int main(void)
{//until C++20Value v{};v.unit = "euro";//since C++20Value v1{100}; // OK (not designated initializers)Value v2{.amount = 100, .unit{"Euro"}}; // OK (second member has default value)Value v3{.precision{8}, .unit = "$"}; // OK (first member has default value)//Value v4{100, .unit = "Euro"}; // ERROR: all or none designated//Value v5{.unit = "$", .amount = 20}; // ERROR: invalid order//Value v6(.amount = 29.9, .unit = "Euro"); // ERROR: only supported for curly braces}
与编程语言C 相比,指定初始化器遵循成员顺序的限制,可以用于所有参数,也可以不用于参
数,不支持直接嵌套,不支持数组。尊重成员顺序的原因是确保初始化反映了调用构造函数的顺序
(与调用析构函数的顺序相反)。
作为使用=、{} 和联合体嵌套初始化的示例:
struct Sub
{double x = 0;int y = 0;
};struct Data
{std::string name;Sub val;
};
不能直接嵌套指定初始化式:
Data d2{.val.y = 42}; // ERROR
用圆括号聚合初始化
假设已经声明了以下集合:
struct Aggr
{std::string msg;int val;
};
C++20 之前,只能使用花括号来初始化带有值的聚合:
Aggr a0; // OK, but no initializationAggr a1{}; // OK, value initialized with "" and 0Aggr a2{"hi"}; // OK, initialized with "hi" and 0Aggr a3{"hi", 42}; // OK, initialized with "hi" and 42Aggr a4 = {}; // OK, initialized with "" and 0Aggr a5 = {"hi"}; // OK, initialized with "hi" and 0Aggr a6 = {"hi", 42}; // OK, initialized with "hi" and 42
自C++20 起,还可以使用圆括号作为外部字符来直接初始化,不带=:
Aggr a7("hi"); // OK since C++20: initialized with "hi" and 0
Aggr a8("hi", 42); // OK since C++20: initialized with "hi" and 42
Aggr a9({"hi", 42}); // OK since C++20: initialized with "hi" and 42
使用= 或内括号仍然不起作用:
Aggr a10 = "hi"; // ERROR
Aggr a11 = ("hi", 42); // ERROR
Aggr a12(("hi", 42)); // ERROR
使用内括号甚至可以编译,其用作使用逗号操作符的表达式周围的圆括号。
现在可以使用圆括号来初始化边界未知的数组:
int a1[]{1, 2, 3}; // OK since C++11
int a2[](1, 2, 3); // OK since C++20
int a3[] = {1, 2, 3}; // OK
int a4[] = (1, 2, 3); // still ERROR
然而,不支持” 省略大括号”(没有嵌套的大括号可以省略):
struct Arr
{int elem[10];
};Arr arr1{1, 2, 3}; // OK
Arr arr2(1, 2, 3); // ERROR
Arr arr3{{1, 2, 3}}; // OK
Arr arr4({1, 2, 3}); // OK (even before C++20)
要初始化std::arrays,仍须使用大括号:
std::array<int,3> a1{1, 2, 3}; // OK: shortcut for std::array{{1, 2, 3}}
std::array<int,3> a2(1, 2, 3); // still ERROR
用圆括号初始化聚合的原因
支持带圆括号的聚合初始化的原因是,可以用圆括号调用new 操作符:
struct Aggr
{std::string msg;int val;
};auto p1 = new Aggr{"Rome", 200}; // OK since C++11
auto p2 = new Aggr("Rome", 200); // OK since C++20 (ERROR before C++20)
这有助于支持在类型中使用聚合,这些类型在内部调用new 时使用括号将值存储在现有内存
中,就像容器和智能指针的情况一样。自C++20 起,以下是可能的情况:
• 现在可以对聚合使用std::make_unique<>() 和std::make_shared<>():
auto up = std::make_unique<Aggr>("Rome", 200); // OK since C++20
auto sp = std::make_shared<Aggr>("Rome", 200); // OK since C++20
C++20 之前,无法对聚合使用这些辅助函数。
• 现在可以将新值放置到聚合容器中:
std::vector<Aggr> cont;
cont.emplace_back("Rome", 200); // OK since C++20
仍然有一些类型不能用括号初始化,但可以用花括号初始化: 作用域枚举(枚举类类型)。类型
std::byte (C++17 引入) 就是一个例子:
std::byte b1{0}; // OK
std::byte b2(0); // still ERROR
auto upb2 = std::make_unique<std::byte>(0); // still ERROR
auto upb3 = std::make_unique<std::byte>(std::byte{0}); // OK
对于std::array,仍然需要大括号(如上所述):
std::vector<std::array<int, 3>> ca;ca.emplace_back(1, 2, 3); // ERRORca.emplace_back({1, 2, 3}); // ERRORca.push_back({1, 2, 3}); // still OK
详细地用圆括号聚合初始化
引入圆括号初始化的建议列出了以下设计准则:
• Type(val) 的现有含义都不应该改变。
• 括号初始化和括号初始化应尽可能相似,但也应尽可能不同,以符合现有的括号列表和括号
列表的模型。
实际上,带花括号的聚合初始化和带圆括号的聚合初始化有以下区别:
• 带圆括号的初始化不会检测窄化转换。
• 用圆括号初始化允许所有隐式转换(不仅是从派生类到基类)。
• 当使用括号时,引用成员不会延长传递的临时对象的生命周期.
• 使用圆括号不支持大括号省略(使用它们就像向形参传递实参一样)。
• 带空括号的初始化甚至适用于显式成员。
• 使用圆括号不支持指定初始化式。
缺少检测窄化的例子如下:
struct Aggr
{std::string msg;int val;
};Aggr a1{"hi", 1.9}; // ERROR: narrowing
Aggr a2("hi", 1.9); // OK, but initializes with 1std::vector<Aggr> cont;
cont.emplace_back("Rome", 1.9); // initializes with 1
emplace 函数永远不会检测窄化。
处理隐式转换时的区别示例如下:
//example of the difference when dealing with implicit conversions is this:
struct Other {
...
operator Aggr(); // defines implicit conversion to Aggr
};Other o;
Aggr a7{o}; // ERROR: no implicit conversion supported
Aggr a8(o); // OK, implicit conversion possible
聚合本身不能定义转换,因为聚合不能有用户定义的构造函数。
缺少大括号省略和大括号初始化的复杂规则,会导致以下行为:
Aggr a01{"x", 65}; // init string with "x" and int with 65
Aggr a02("x", 65); // OK since C++20 (same effect)Aggr a11{{"x", 65}}; // runtime ERROR: "x" doesn’t have 65 characters
Aggr a12({"x", 65}); // OK even before C++20: init string with "x" and int with 65Aggr a21{{{"x", 65}}}; // ERROR: cannot initialize string with initializer list of
,→ "x" and 65
Aggr a22({{"x", 65}}); // runtime ERROR: "x" doesn’t have 65 charactersAggr a31{'x', 65}; // ERROR: cannot initialize string with ’x’Aggr a32('x', 65); // ERROR: cannot initialize string with ’x’Aggr a41{{'x', 65}}; // init string with ’x’ and char(65)Aggr a42({'x', 65}); // OK since C++20 (same effect)Aggr a51{{{'x', 65}}}; // init string with ’x’ and char(65)Aggr a52({{'x', 65}}); // OK even before C++20: init string with ’x’ and 65Aggr a61{{{{'x', 65}}}}; // ERRORAggr a62({{{'x', 65}}}); // OK even before C++20: init string with ’x’ and 65
当执行复制初始化(用= 初始化) 时,显式很重要,使用空括号可能会产生不同的效果:
struct C
{explicit C() = default;
};struct A // aggregate
{int i;C c;
};auto a1 = A{42, C{}}; // OK: explicit initialization
auto a2 = A(42, C()); // OK since C++20: explicit initializationauto a3 = A{42}; // ERROR: cannot call explicit constructor
auto a4 = A(42); // ERROR: cannot call explicit constructorauto a5 = A{}; // ERROR: cannot call explicit constructor
auto a6 = A(); // OK: can call explicit constructor
这在C++20 中并不新鲜,在C++20 之前就已经支持a6 的初始化了。
最后,若对具有右值引用成员的聚合使用圆括号进行初始化,则初始值的生存期不会延长,所
以不应该传递临时对象(右值):
struct A
{int a;int&& r;
};int f();
int n = 10;A a1{1, f()}; // OK, lifetime is extended
std::cout << a1.r << '\n'; // OK
A a2(1, f()); // OOPS: dangling reference
std::cout << a2.r << '\n'; // runtime ERROR
A a3(1, std::move(n)); // OK as long as a3 is used only while n exists
std::cout << a3.r << '\n'; // OK
#include <iostream>
#include <string>
#include <memory>struct Widget{Widget() { std::cout << "ctor" << std::endl;str = std::make_unique<std::string>("test:temporary do not extend rvalue reference member lifetime");}~Widget(){std::cout << "dtor" << std::endl; str.reset();}friend std::ostream& operator << (std::ostream& os, const Widget& w){ return os << *(w.str);}
private:std::unique_ptr<std::string> str{};
};
struct A {int a;Widget&& r;
};Widget f() { return Widget{};}void test_rvalue_ref_mem(){Widget n{};A a1{1, f()}; // OK, lifetime is extendedstd::cout << a1.r << std::endl; // OKA a3(1, std::move(n)); // OK as long as a3 is used only while n existsstd::cout << a3.r << std::endl; // OK
}
void test_temporary_if_extend_rvalue_reference_member_lifetime()
{std::cout << "----------------------------" << std::endl;A a2(1, f()); // OOPS: dangling referencestd::cout << "----------------------------" << std::endl;std::cout << a2.r << std::endl; // runtime ERRORstd::cout << "bug! bug! it is too bad!" << std::endl;
}
int main()
{test_rvalue_ref_mem();test_temporary_if_extend_rvalue_reference_member_lifetime();return 0;
}
由于这些复杂的规则和陷阱,只有在必要时才应该使用带圆括号的聚合初始化,比如在使用
std::make_unique<>()、std::make_shared<>() 或emplace 函数时。
相关文章:
C++20 指定初始化器
指定初始化器 对于聚合,C20 提供了一种方法来指定应该用传递的初始值初始化哪个成员,但只能使用它 来跳过参数。 假设有以下聚合类型: struct Value {double amount{0};int precision{2};std::string unit{"Dollar";}; }; 那么&#x…...
【从零开始学习计算机科学】设计模式(二)工厂模式、抽象工厂模式、单例模式、建造者模型、原型模式
【从零开始学习计算机科学】设计模式(二)工厂模式、抽象工厂模式、单例模式、建造者模型、原型模式 工厂模式主要特点类型适用场景抽象工厂模式主要特点工作原理适用场景举例优点缺点总结单例模式主要特点工作原理适用场景优点缺点总结建造者模式主要特点工作原理适用场景优点…...
视频翻译器免费哪个好?轻松玩转视频直播翻译
你是不是觉得看外语视频很麻烦?每次遇到喜欢的外语电影、电视剧或动漫,总是要等字幕组的翻译,或者因为语言不通而错过精彩的情节。 这个时候,掌握多语种直播翻译方案就显得尤为重要,有了实时字幕,看外语视…...
mysql 数据库异常排查
1、简单查询语句无法执行 线上问题记录,有个删除语句走不了索引导致锁表,其他所有引用该表的操作需要等待删除成功后才能执行,导致服务不可用 排查思路,先看是否有长时间未提交的事务 SELECT * FROM information_schema.INNOD…...
Python列表1
# coding:utf-8 print("———————————— 列表 ——————————————")列表 是指一系列按照特定顺序排列的元素组成 是Python中内置的可变序列 使用[]定义列表,元素与元素之间使用英文的逗号分隔 列表中的元素可以是任意的数据类型列表的…...
如何在前端发版时实现向客户端推送版本更新消息
前端打包发版后如何用户一个更新提示,该提示会让用户主动去更新当前正在操作的页面,或者在系统有较大更新时,让用户重新更新本地缓存信息,这一操作比较友好,且能避免用户不更新当前系统,导致某些接口依赖更新后的数据而导致接口请求失败报错。 1、后端更新提示 有些情况…...
Redis系列:深入理解缓存穿透、缓存击穿、缓存雪崩及其解决方案
在使用Redis作为缓存系统时,我们经常会遇到“缓存穿透”、“缓存击穿”和“缓存雪崩”等问题,这些问题一旦出现,会严重影响应用性能甚至造成服务不可用。因此,理解这些问题的产生原因和解决方案非常重要。 本文将全面讲解缓存穿透…...
3.19学习总结
学习了Java中的面向对象的知识点 完成一道算法题,找树左下角的值,错误的以为左下角只能是最底层的左节点,但指的是最底层最左边的节点...
服务创造未来 东隆科技携多款产品亮相慕尼黑
慕尼黑上海光博会依托于德国慕尼黑博览集团,自2006年首次举办以来,始终坚持将国内外先进的光电技术成果展示给观众,深度链接亚洲乃至全球的激光、光学、光电行业的优质企业及买家。如今已经成为了国内外专业观众信赖的亚洲激光、光学、光电行…...
AI 时代,学习 Java 应如何入手?
一、Java 的现状:生态繁荣与 AI 融合的双重机遇 在 2025 年的技术版图中,Java 依然稳坐企业级开发的 “头把交椅”。根据行业统计,Java 在全球企业级应用中的市场份额仍超过 65%,尤其在微服务架构、大数据平台和物联网࿰…...
LiteratureReading:[2016] Enriching Word Vectors with Subword Information
文章目录 一、文献简明(zero)二、快速预览(first)1、标题分析2、作者介绍3、引用数4、摘要分析(1)翻译(2)分析 5、总结分析(1)翻译(2)…...
JavaScript 实现导出内容自动居中:从原理到实践
引言 在前端开发中,我们经常会遇到需要将页面上的内容导出为文件(如 PDF、Excel 等)的需求。而在导出的内容中,让元素自动居中显示可以提升内容的美观度和专业性。本文将围绕 JavaScript 实现导出内容自动居中展开,详…...
Docker 速通(总结)
Docker 命令 镜像 docker build: 从 Dockerfile 构建镜像。docker pull: 从 Docker Hub 或其他注册表拉取镜像。docker push: 将镜像推送到 Docker Hub 或其他注册表。docker images: 列出本地镜像。docker rmi: 删除本地镜像。 容器 docker run: 创建并启动一个新的容器。…...
人工智能之数学基础:矩阵的降维
本文重点 在现实世界中,我们经常会遇到高维数据。例如,图像数据通常具有很高的维度,每个像素点都可以看作是一个维度。高维数据不仅会带来计算和存储上的困难,还可能会导致 “维数灾难”,即随着维度的增加,数据的稀疏性和噪声也会增加,从而影响数据分析的效果。因此,我…...
Object 转 JSONObject 并排除null和““字符串
public static JSONObject objToJSONObject(Object obj) throws Exception{//创建一个 HashMap 对象 map,用于存储对象的属性名和属性值。//key 是属性名(String 类型),value 是属性值(Object 类型)Map<…...
mysql5.7主从部署(docker-compose版本)
mysql5.7主从部署(docker-compose版本) 1:docker-compose-test.yml 文件信息 version: 3services:# MySQL 数据库mysql-master:image: mysql:5.7container_name: mysql-masterenvironment:MYSQL_ROOT_PASSWORD: 123456MYSQL_DATABASE: nacosports:- 23…...
Java+Html实现前后端客服聊天
文章目录 核心组件网络通信层事件调度层服务编排层 Spring实现客服聊天技术方案对比WebScoket建立连接用户上线实现指定用户私聊群聊离线 SpringBootWebSocketHtmljQuery实现客服聊天1. 目录结构2. 配置类3. 实体类、service、controller4. ChatWebSocketHandler消息处理5.前端…...
实用工具-Another Redis Desktop Manager介绍
GitHub:https://github.com/qishibo/AnotherRedisDesktopManager/releases Gitee:AnotherRedisDesktopManager 发行版 - Gitee.com Another Redis Desktop Manager 是一款免费的 Redis 可视化管理工具,具有以下特点和功能: 特…...
MySQL如何存储表情符号?
存储表情符号 默认mysql的字符集是utf8,排序规则为 utf8_general_ci INSERT INTO department (name) VALUES (😄)在存储表情的时候会报 1366 - Incorrect string value: \xF0\x9F\x98\x84 for column name at row 1, Time: 0.007000s 这时需要修改字符…...
解锁 DeepSeek 安全接入、稳定运行新路径
背景 目前,和 DeepSeek 相关的需求总结为两类: 因官方 APP/Web 服务经常无法返回结果,各类云厂商、硬件或软件企业提供满血版或蒸馏版的 API 算力服务,还有不少基于开源家用计算和存储设备的本地部署方案,以分担 De…...
Spring Boot 配置属性 (Configuration Properties) 详解:优雅地管理应用配置
引言 Spring Boot 的 配置属性 (Configuration Properties) 是其另一个核心特性,它提供了一种 类型安全、结构化 的方式来管理应用的配置信息。 与自动配置相辅相成,配置属性允许开发者 以声明式的方式将外部配置 (如 properties 文件、YAML 文件、环境…...
【LangChain入门 1】安装
文章目录 一、安装LangChain二、安装Ollama三、Ollama下载DeepSeekR1-7b模型 本学习系列以Ollama推理后端作为大语言模型,展开对LangChain框架的入门学习。 模型采用deepseek-r1:7b。 毕竟是免费开源的,下载过程耐心等待即可。 如果可以连接外网&#x…...
HTML中required与aria required区别
在HTML中,required和aria-required"true"都用于标识表单字段为必填项,但它们的作用和适用场景有所不同: 1. required 属性 • 功能属性:属于HTML5原生属性,直接控制表单验证逻辑。 • 作用: • …...
IvorySQL 增量备份与合并增量备份功能解析
1. 概述 IvorySQL v4 引入了块级增量备份和增量备份合并功能,旨在优化数据库备份与恢复流程。通过 pg_basebackup 工具支持增量备份,显著降低了存储需求和备份时间。同时,pg_combinebackup 工具能够将多个增量备份合并为单个完整备份&#x…...
【css酷炫效果】纯CSS实现故障文字特效
【css酷炫效果】纯CSS实现故障文字特效 缘创作背景html结构css样式完整代码基础版进阶版(3D效果) 效果图 想直接拿走的老板,链接放在这里:https://download.csdn.net/download/u011561335/90492053 缘 创作随缘,不定时更新。 创作背景 刚…...
SpringSecurity配置(自定义认证过滤器)
文末有本篇文章的项目源码文件可供下载学习 在这个案例中,我们已经实现了自定义登录URI的操作,登录成功之后,我们再次访问后端中的API的时候要在请求头中携带token,此时的token是jwt字符串,我们需要将该jwt字符串进行解析,查看解析后的User对象是否处于登录状态.登录状态下,将…...
设计模式(行为型)-备忘录模式
目录 定义 类图 角色 角色详解 (一)发起人角色(Originator) (二)备忘录角色(Memento) (三)备忘录管理员角色(Caretaker)…...
WebAssembly 技术在逆向爬虫中的应用研究
一、引言 1.1 Web 技术发展与性能需求 在当今数字化浪潮中,Web 应用已成为人们生活和工作中不可或缺的一部分。从简单的静态网页到功能复杂的单页面应用(SPA),Web 技术的发展日新月异。随着用户对 Web 应用交互性、实时性和复杂性的要求不断提高,传统的 Web 开发技术面临着…...
Advanced Intelligent Systems 软体机器手助力截肢者玩转鼠标
随着科技的不断进步,假肢技术在改善截肢者生活质量方面取得了显著成就。然而,截肢群体在就业方面仍面临巨大困难,适龄截肢群体的就业率仅为健全群体的一半。现有的肌电控制假肢手在与计算机交互时存在诸多挑战,特别是截肢者在使用…...
pyhton中 字典 元组 列表 集合之间的互相转换
在 Python 中,集合(set)、字典(dict)、元组(tuple)、列表(list)和序列(如字符串 str)之间可以互相转换。以下是它们之间转换的详细方法,涵盖从基础到高级的用法。 1. 列表(list)与其他类型的转换 1.1 列表 → 集合 my_list = [1, 2, 2, 3...
