C++20尝鲜:聚合体初始化变化 聚合体的定义
bigegpt 2024-10-16 07:54 1 浏览
C++20 功能特性 | 提案 |
指派初始化器 | P0329R4 |
括号形式的聚合体初始化 | P0960R3 |
禁止有用户声明构造函数的聚合体 | P1008R1 |
聚合类的类模板实参推导 | P1816R0 |
聚合体
- 数组类型
- 符合以下条件的类类型(常为 struct 或 union)
没有私有或受保护的直接 (C++17 起)非静态数据成员 | |
没有用户声明的构造函数 | (C++11 前) |
没有用户提供的构造函数(允许显式预置或弃置的构造函数) | (C++11 起) |
没有用户提供、继承或 explicit 的构造函数(允许显式预置或弃置的构造函数) | (C++17 起) |
没有用户声明或继承的构造函数 | (C++20 起) |
| |
没有虚成员函数 | |
| (C++11 起) |
聚合体初始化
T 对象 = {实参1, 实参2, ...}; | (1) | |
T 对象 {实参1, 实参2, ... }; | (2) | (C++11 起) |
T 对象 = { .指派符 = 实参1 , .指派符 { 实参2 } ... }; | (3) | (C++20 起) |
T 对象 { .指派符 = 实参1 , .指派符 { 实参2 } ... }; | (4) | (C++20 起) |
T 对象 (实参1, 实参2, ...); | (5) | (C++20 起) |
聚合类的类模板实参推导
在C++17中使用带CTAD的聚合,我们需要显式的推导指引,现在没有必要了。
template<typename T, typename U>
struct S{
T t;
U u;
};
// 在 C++17,需要推导指引
// template<typename T, typename U>
// S(T, U) -> S<T,U>;
int main(){
S s{1, 2.0}; // S<int, double>
}
如果有用户提供了推导指引,则不参与CTAD
#include <string>
template<typename T>
struct MyData{
T data;
};
MyData(const char*) -> MyData<std::string>;
int main(){
MyData s1{"abc"}; // OK, MyData<std::string> 使用推导指引
MyData<int> s2{1}; // OK, :显式模板参数
// MyData s3{1}; // Error, CTAD 不参与
}
可以推导数组类型
#include <cstdio>
template<typename T, std::size_t N>
struct Array{
T data[N];
};
int main(){
Array a{{1, 2, 3}}; // Array<int, 3>, 注意额外的括号
Array str{"hello"}; // Array<char, 6>
}
大括号省略不适用于待决名的非数组类型或待决名边界的数组类型(待决名dependent name)
#include <cstdio>
template<typename T, typename U>
struct Pair{
T first;
U second;
};
template<typename T, std::size_t N>
struct A1{
T data[N];
T oneMore;
Pair<T, T> p;
};
template<typename T>
struct A2{
T data[3];
T oneMore;
Pair<int, int> p;
};
int main(){
// A1::data 是待决名带边界的数组类型 , A1::p 是待决名类型, 大括号省略不支持
A1 a1{{1,2,3}, 4, {5, 6}}; // A1<int, 3>
// A2::data 是待决名不带边界的数组类型 , A2::p 不是是待决名类型, 大括号省略支持
A2 a2{1, 2, 3, 4, 5, 6}; // A2<int>
}
适用于包扩展。一个扩展包的尾随聚合元素对应于所有剩余元素
#include <iostream>
template<typename... Ts>
struct Overload : Ts...{
using Ts::operator()...;
};
// C++20不需要推到指引
Overload p{[](int){
std::cout << "called with int" << std::endl;
}, [](char){
std::cout << "called with char" << std::endl;
}
}; // Overload<lambda(int), lambda(char)>
int main(){
p(1); // int
p('c'); // char
}
无尾随元素包展开对应于没有元素
template<typename T, typename...Ts>
struct Pack : Ts... {
T x;
};
// 只能推导首元素
int main()
{
Pack p1{1}; // Pack<int>
Pack p2{[]{}}; // Pack<lambda()>
// Pack p3{1, []{}}; // error
}
包中的元素数量只被推导一次,但如果重复,类型应该完全匹配:
#include <tuple>
struct A{};
struct B{};
struct C{};
struct D{
operator C(){return C{};}
};
template<typename...Ts>
struct P : std::tuple<Ts...>, Ts...{
};
int main(){
P a {std::tuple<A, B, C>{}, A{}, B{}, C{}}; // P<A, B, C>
// equivalent to the above, since pack elements were deduced for
// std::tuple<A, B, C> there's no need to repeat their types
P b {std::tuple<A, B, C>{}, {}, {}, {}}; // P<A, B, C>
// since we know the whole P<A, B, C> type after std::tuple initializer, we can
// omit trailing initializers, elements will be value-initialized as usual
P c {std::tuple<A, B, C>{}, {}, {}}; // P<A, B, C>
// error, pack deduced from first initializer is <A, B, C> but got <A, B, D> for
// the trailing pack, implicit conversions are not considered
P d {std::tuple<A, B, C>{}, {}, {}, D{}};
}
https://wandbox.org/nojs/gcc-head
https://wandbox.org/nojs/clang-head
禁止使用用户声明的构造函数聚合
现在聚合类型不能有用户声明的构造函数。以前,聚合只允许有删除的或默认的构造函数。这导致了带有默认/删除构造函数的聚合的怪异行为(它们是用户声明的,而不是用户提供的)
// 以下类型在c++ 20中都不是聚合
struct S{
int x{2};
S(int) = delete; // 用户声明的构造函数
};
struct X{
int x;
X() = default; // // 用户声明的构造函数
};
struct Y{
int x;
Y(); // // 用户声明的构造函数
};
Y::Y() = default;
int main(){
S s(1); // 一直都是错误
S s2{1}; // C++17正确, C++20错误
X x{1}; // C++17正确, C++20错误
Y y{2}; // 一直都是错误
}
https://wandbox.org/permlink/cPA3m3ppHv1h8eIT
圆括号的聚会初始化
圆括号的聚合初始化现在与花括号初始化的工作方式相同,但是允许窄化转换,不允许指定的初始化器,不允许为临时对象延长生命周期,也不允许大括号省略。没有初始化式的元素是值初始化的。这允许无缝使用工厂函数,如std::make_unique<>()/emplace()。
#include <memory>
struct S{
int a;
int b = 2;
struct S2{
int d;
} c;
};
struct Ref{
const int& r;
};
int GetInt(){
return 21;
}
int main(){
//S{0.1}; // error, 花括号不允许窄化(narrowing)
S(0.1); // OK
S{.a=1}; // OK
//S(.a=1); // error, 圆括号不允许指定初始化
Ref r1{GetInt()}; // OK, 生命周期被延长
Ref r2(GetInt()); // 危险, 生命周期没被延长
S{1, 2, 3}; // OK,花括号省略, 相当于 S{1,2,{3}}
//S(1, 2, 3); // error, 不支持括号省略
// 没有初始化器的值接受默认值或值初始化(T{})
S{1}; // {1, 2, 0}
S(1); // {1, 2, 0}
// make_unique 正常工作
auto ps = std::make_unique<S>(1, 2, S::S2{3});
// 数组也支持了
int arr1[](1, 2, 3);
int arr2[2](1); // {1, 0}
}
https://wandbox.org/permlink/w8OrhnuA6WJLb4GA
指派初始化器
每个 指派符 必须指名 T 的一个直接非静态数据成员,而表达式中所用的所有 指派符 必须按照与 T 的数据成员相同的顺序出现。
struct A { int x; int y; int z; };
A a{.y = 2, .x = 1}; // 错误:指派符的顺序不匹配声明顺序
A b{.x = 1, .z = 2}; // OK:b.y 被初始化为 0
指派初始化器所指名的每个直接非静态数据成员,从其指派符后随的对应花括号或等号初始化器初始化。禁止窄化转换。
指派初始化器可用于将联合体初始化为其首个成员之外的状态。只可以为一个联合体提供一个初始化器。
union u { int a; const char* b; };
u f = { .b = "asdf" }; // OK:联合体的活跃成员为 b
u g = { .a = 1, .b = "asdf" }; // 错误:只可提供一个初始化器
对于非联合体的聚合体中未提供指派初始化器的元素,按上述针对初始化器子句的数量少于成员数量时的规则进行初始化(如果提供默认成员初始化器则使用它,否则为空列表初始化):
struct A {
string a;
int b = 42;
int c = -1;
};
A{.c=21} // 以 {} 初始化 a,这样会调用默认构造函数
// 然后以 = 42 初始化 b
// 然后以 = 21 初始化 c
如果以指派初始化器子句初始化的聚合体拥有一个匿名联合体成员,那么对应的指派初始化器必须指名该匿名联合体的其中一个成员。
注意:乱序的指派初始化、嵌套的指派初始化、指派初始化器与常规初始化器的混合,以及数组的指派初始化在 C 编程语言中受支持,但在 C++ 不允许。
struct A { int x, y; };
struct B { struct A a; };
struct A a = {.y = 1, .x = 2}; // 合法 C,非法 C++(乱序)
int arr[3] = {[1] = 5}; // 合法 C,非法 C++(数组)
struct B b = {.a.x = 0}; // 合法 C,非法 C++(嵌套)
struct A a = {.x = 1, 2}; // 合法 C,非法 C++(混合)
相关推荐
- 无畏契约手游测试资格获取方法,安卓IOS下载教程
-
《无畏契约:源能行动》是拳头游戏与腾讯光子工作室联合开发的《无畏契约》正版手游,延续了端游的5v5战术射击核心玩法,并针对移动端进行了操作优化。游戏以快节奏的爆破模式为核心,融合角色技能系统、经济策略...
- 微软正在测试重新设计的Office图标 但您现在可以提前下载重制版本
-
今年4月,有消息称微软正在征求用户对一组Office图标7年来首次重制版的看法(上一次重制是在2018年末)。现在,有人决定自己动手,制作了一套微软的高分辨率图标包与用户共享以获得反馈。Reddi...
- AB Download Manager:一款可以替代IDM的开源桌面下载管理器
-
软件介绍IDM下载器大家应该多少都知道一点,如果不知道的话只能自行百度了,但是IDM本身是需要付费的,而今天推荐的这款软件,在下载方面是和IDM差不多的,大概有90%的相似度,感兴趣的朋友可以体验一下...
- 《夺宝奇兵》PS5光盘仅20G:其余需联网下载
-
来源:游民星空【《夺宝奇兵》PS5光盘仅20G:其余需联网下载】据游戏测试账号“DoesItPlay1”在推特发布动态表示,《夺宝奇兵:古老之圈》PS5实体光盘只存储了20GB的游戏数据,其余内容需要...
- 薇姐聊诗词7:诗词创作韵部查询及检测工具
-
薇姐聊诗词7:诗词创作韵部查询及检测工具。·1、诗词创作中所用韵脚哪里找?平水韵:106部,分平声30部、上声29部、去声30部、入声17部,反映中古汉语语音体系。新韵:(中华新韵)14部,以普通话为...
- 阿里云国际站:怎样模拟高并发测试场景?
-
本文由【云老大】TG@yunlaoda360撰写一、使用JMeter安装JMeter:从JMeter官网下载并安装JMeter。创建测试计划:打开JMeter,创建一个新的测试计划。添加线程组...
- Android Studio 新增 AI 驱动的测试和更智能的崩溃诊断功能
-
随着GoogleI/O2025大会的落幕,值得注意的是,谷歌在AndroidStudio中引入了几项新功能,旨在改善Android应用程序的开发流程。最新版本集成了更先进的AI工...
- 如何在本地测试PHP源码的网站
-
通常,我们测试自建网站或从网上获取的PHP源码时,若直接上传到服务器,出错后再修改会很麻烦,因此一般会选择先在本地电脑上进行测试。1、先下载喜欢的源码,很多网站提供下载,如源码论坛等。这些源码是现成...
- 显卡性能测试工具3DMark06的应用教程
-
显卡作为计算机的重要组成部分,也是主要的输出设备。在计算机系统中,图形处理性能的瓶颈往往在于显卡。若要评估显卡性能,用户可以借助专业的检测工具3DMark,判断显卡是否能满足当前需求,或者是否需要...
- Downie4 安装教程(轻松获取视频素材)
-
效果一、准备工作下载软件链接:http://www.macfxb.cn二、开始安装1、双击运行软件,将其从左侧拖入右侧文件夹中,等待安装完毕2、应用程序显示软件图标,表示安装成功三、运行测试1、打开软...
- 如何使用瑞星杀毒软件的网速测试功能
-
下面为大家介绍瑞星杀毒软件的网速测试功能。1、打开安全工具,找到网速测试,点击下载后开启。2、打开网速测试页面,点击开始测试按钮。3、测试结束后,你就能知晓自己的网速了。(9744667)...
- 阿里云国际站:如何测试服务器真实带宽?
-
本文由【云老大】TG@yunlaoda360撰写基于命令行工具测试iperf/iperf3:服务器端:在服务器上安装iperf后,运行iperf-s或iperf3-s启动服务端,...
- CentOS Docker 安装
-
Docker支持以下的64位CentOS版本:CentOS9(stream)更高版本...必须启用centos-extras仓库,该仓库默认启用,如果您禁用了它,需要重新启用。使用官...
- Fast YOLO:用于实时嵌入式目标检测(附论文下载)
-
关注并星标从此不迷路计算机视觉研究院公众号ID|ComputerVisionGzq计算机视觉研究院专栏作者:Edison_G目标检测被认为是计算机视觉领域中最具挑战性的问题之一,因为它涉及场景中对象分...
- aigc检测报告与查重监测报告
-
哈喽学妹学弟们!最近是不是都在忙着写论文呢?记得当初我写论文的时候,也被AIGC检测报告和查重监测报告搞得晕头转向。不过经过我的一番摸索,终于搞清楚了它们之间的区别和联系。来来来,学姐今天就来给你们传...
- 一周热门
- 最近发表
- 标签列表
-
- mybatiscollection (79)
- mqtt服务器 (88)
- keyerror (78)
- c#map (65)
- resize函数 (64)
- xftp6 (83)
- bt搜索 (75)
- c#var (76)
- mybatis大于等于 (64)
- xcode-select (66)
- mysql授权 (74)
- 下载测试 (70)
- httperror403.14-forbidden (63)
- logstashinput (65)
- hadoop端口 (65)
- dockernetworkconnect (63)
- esxi7 (63)
- vue阻止冒泡 (67)
- oracle时间戳转换日期 (64)
- jquery跨域 (68)
- php写入文件 (73)
- kafkatools (66)
- mysql导出数据库 (66)
- jquery鼠标移入移出 (71)
- 取小数点后两位的函数 (73)