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。
注意以下限制:

  1. 必须使用= 或{} 传递初始值。
  2. 可以跳过会员,但必须遵循其顺序。按名称初始化的成员的顺序必须与声明中的顺序匹配。
  3. 必须对所有参数使用指定初始化式,或者不使用指定初始化式。不允许混合初始化。
  4. 不支持对数组使用指定初始化。
  5. 可以使用指定初始化式嵌套初始化,但不能直接使用.mem.mem。
  6. 初始化带有圆括号的聚合时,不能使用指定初始化器。
  7. 指定初始化式也可以用在联合体中。

例如:

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 函数时。

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

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

相关文章

JUC大揭秘:从ConcurrentHashMap到线程池,玩转Java并发编程!

目录 JUC实现类 ConcurrentHashMap 回顾HashMap ConcurrentHashMap CopyOnWriteArrayList 回顾ArrayList CopyOnWriteArrayList: CopyOnWriteArraySet 辅助类 CountDownLatch 线程池 线程池 线程池优点 ThreadPoolExecutor 构造器各个参数含义&#xff1a; 线程…

【unity实战】用unity封装一个复杂全面且带不同射击模式的飞机大战射击系统

考虑到每个人基础可能不一样,且并不是所有人都有同时做2D、3D开发的需求,所以我把 【零基础入门unity游戏开发】 分为成了C#篇、unity通用篇、unity3D篇、unity2D篇。 【C#篇】:主要讲解C#的基础语法,包括变量、数据类型、运算符、流程控制、面向对象等,适合没有编程基础的…

【AWS入门】Amazon EC2简介

【AWS入门】Amazon EC2简介 A Brief Introduction to Amazon EC2 By JacksonML 1. 背景 众所周知&#xff0c;互联网时代的用户每天需要访问Web站点&#xff0c;以获取不同的信息和数据。而海量的Web站点&#xff0c;其内容均存放在服务器上&#xff0c;无论服务器有多远&am…

PyTorch系列教程:基于LSTM构建情感分析模型

情感分析是一种强大的自然语言处理&#xff08;NLP&#xff09;技术&#xff0c;用于确定文本背后的情绪基调。它常用于理解客户对产品或服务的意见和反馈。本文将介绍如何使用PyTorch和长短期记忆网络&#xff08;LSTMs&#xff09;创建一个情感分析管道&#xff0c;LSTMs在处…

Vue 渲染 LaTeX 公式 Markdown 库

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…

如何在WordPress中添加下载链接?

在WordPress网站上添加文件下载链接&#xff0c;不仅能提升用户体验&#xff0c;还能增加网站的互动性和实用价值。不管是提供免费的电子书、软件&#xff0c;还是其他类型的文件&#xff0c;下载链接都可以让用户快速获取所需的资源&#xff0c;增强他们对网站的好感。 本文将…

C/C++ 内存管理

1.C/C内存分布 sizeof和strlen有什么区别&#xff1a; 本质区别 特性sizeofstrlen类型运算符&#xff08;编译时计算&#xff09;库函数&#xff08;运行时计算&#xff09;作用对象变量、数据类型、表达式仅限以 \0 结尾的字符串&#xff08;char* 或字符数组&#xff09;功…

【C语言】:学生管理系统(多文件版)

一、文件框架 二、Data data.txt 三、Inc 1. list.h 学生结构体 #ifndef __LIST_H__ #define __LIST_H__#include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdbool.h> #include <time.h>#define MAX_LEN 20// 学生信息…

【Spring】第三弹:基于 XML 获取 Bean 对象

一、获取 Bean 对象 1.1 根据名称获取 Bean 对象 由于 id 属性指定了 bean 的唯一标识&#xff0c;所以根据 bean 标签的 id 属性可以精确获取到一个组件对象。 1.确保存在一个测试类&#xff1a; public class HelloWorld {public void sayHello(){System.out.println(&quo…

Easysearch 索引生命周期管理实战

如果你的使用场景是对时序型数据进行分析&#xff0c;可能你会更重视最新的数据&#xff0c;并且可能会定期对老旧的数据进行一些处理&#xff0c;比如减少副本数、forcemerge、 删除等。Easysearch 的索引生命周期管理功能&#xff0c;可以自动完成此类索引的管理任务。 创建…

ARMv8.x-M架构计算能力概览

1.ARMv8.xM架构提供了哪些计算能力&#xff1f; ARMv7-M时代&#xff0c;Cortex-M系列CPU以提供通用计算能力为主。ARMv8-M架构提供了更加多样的计算能力。 首先&#xff0c;提供Thumb2指令集提供整数通用计算能力。 其次&#xff0c;ARMv8.x-M架构手册明确列出了更多可选的CPU…

20. Excel 自动化:Excel 对象模型

一 Excel 对象模型是什么 Excel对象模型是Excel图形用户界面的层次结构表示&#xff0c;它允许开发者通过编程来操作Excel的各种组件&#xff0c;如工作簿、工作表、单元格等。 xlwings 是一个Python库&#xff0c;它允许Python脚本与Excel进行交互。与一些其他Python库&#x…

大模型GGUF和LLaMA的区别

GGUF&#xff08;Gigabyte-Graded Unified Format&#xff09;和LLaMA&#xff08;Large Language Model Meta AI&#xff09;是两个不同层面的概念&#xff0c;分别属于大模型技术栈中的不同环节。它们的核心区别在于定位和功能&#xff1a; 1. LLaMA&#xff08;Meta的大语言…

一周学会Flask3 Python Web开发-SQLAlchemy查询所有数据操作-班级模块

锋哥原创的Flask3 Python Web开发 Flask3视频教程&#xff1a; 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 我们来新建一个的蓝图模块-班级模块&#xff0c;后面可以和学生模块&#xff0c;实现一对多的数据库操作。 blueprint下新建g…

STM32学习【5】用按键控制LED亮灭(寄存器)以及对位运算的思考

目录 1. 看原理图2 使能GPIOAGPIOA时钟模块2.2 设置引脚GPIO输入2.3 读取引脚值 3. 关于寄存器操作的思考 写在前面 注意&#xff0c;这篇文章虽然说是用按键控制led亮灭&#xff0c;重点不在代码&#xff0c;而是关键核心的描述。 用寄存器的方式&#xff0c;通过key来控制led…

js,html,css,vuejs手搓级联单选

<!DOCTYPE html> <html lang"zh"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>级联选择器</title><script src"h…

【Spring】第四弹:基于XML文件注入Bean对象

一、setter 注入Bean对象 1.创建Student对象 public class Student {private Integer id;private String name;private Integer age;private String sex;public Student() {}public Integer getId() {return id;}public void setId(Integer id) {this.id id;}public String …

DeepSeek私有化部署与安装浏览器插件内网穿透远程访问实战

文章目录 前言1. 本地部署OllamaDeepSeek2. Page Assist浏览器插件安装与配置3. 简单使用演示4. 远程调用大模型5. 安装内网穿透6. 配置固定公网地址 前言 最近&#xff0c;国产AI大模型Deepseek成了网红爆款&#xff0c;大家纷纷想体验它的魅力。但随着热度的攀升&#xff0c…

单目3d detection算法记录

1、centernet object as points 这篇文章的核心单目3d检测主要是利用中心点直接回归出3d模型的所有属性&#xff0c;head共享整个backbone&#xff0c;其中3d属性包括&#xff1a;2d目标中心点、2dw和h、2d offsets、3doffsets、3d dimmession、rot还有depth。 其中对应的dep…

MySQL程序

博主主页: 码农派大星. 数据结构专栏:Java数据结构 数据库专栏:数据库 JavaEE专栏:JavaEE 软件测试专栏:软件测试 关注博主带你了解更多知识 1. mysqld (MySQL服务器) mysqld也被称为MySQL服务器&#xff0c;是⼀个多线程程序&#xff0c;对数据⽬录进⾏访问管理(包含数据库…