Fed*_*dor 7 c++ designated-initializer c++20
在下面的程序中,聚合结构B具有字段a,它本身就是一个聚合。可以使用 C++20 指定初始值设定项设置其值而不用花括号括起来吗?
struct A { int i; };
struct B { A a; };
int main() {
[[maybe_unused]] B x{1}; //ok everywhere
[[maybe_unused]] B y{.a = {1}}; //ok everywhere
[[maybe_unused]] B z{.a = 1}; //ok in MSVC,Clang; error in GCC
}
Run Code Online (Sandbox Code Playgroud)
MSVC 和 Clang 编译器接受此代码。但是 GCC 发出了一个奇怪的错误:
error: 'A' has no non-static data member named 'a'
Run Code Online (Sandbox Code Playgroud)
演示:https : //gcc.godbolt.org/z/65j1sTcPG
是 GCC 中的错误,还是标准不允许这种初始化?
TLDR;GCC 是对的,其他人都错了,因为他们假装指定的初始化列表一直表现得像等效的非指定初始化列表。
要了解这里发生了什么(以及为什么编译器不同意),让我们看一下您的第一个示例:
B x{1};
Run Code Online (Sandbox Code Playgroud)
由于我们使用大括号,列表初始化的规则开始生效。该列表不是指定的初始化列表,因此 3.1 失败。3.2 失败,因为int不是类型B或派生自 的类型B。3.3 失败失败,因为B不是字符数组。最后是3.4,它带我们聚合初始化。
[dcl.init.aggr]/3.2 告诉我们显式初始化的元素B由B::a.
第 4 段告诉我们如何初始化显式初始化的元素。4.1 不适用,因为B不是union. 但也... 4.2 不适用,因为B::a不能从1.
这似乎不应该起作用。幸运的是,第 15 和 16 段存在:
大括号可以在初始化列表中省略,如下所示。如果初始值设定项列表以左大括号开头,则后续的以逗号分隔的初始值设定项列表将初始化子聚合的元素;初始化子句比元素多是错误的。但是,如果子聚合的初始化器列表不以左大括号开头,则仅从列表中获取足够的初始化器子句来初始化子聚合的元素;留下任何剩余的初始化子句来初始化聚合的下一个元素,当前子聚合是该聚合的一个元素。
使用赋值表达式初始化元素时,会考虑所有隐式类型转换 ([conv])。如果赋值表达式可以初始化一个元素,则该元素被初始化。否则,如果元素本身是子聚合,则假定大括号省略并考虑赋值表达式来初始化子聚合的第一个元素。
也就是说,如果初始值设定项A不能通过复制初始化进行初始化,则大括号省略规则开始生效。并且A 可以从 的初始值设定项列表进行初始化{1}。因此它是。
这就是指定初始值设定项有问题的地方。一个designated-initializer-list是没有的initializer-list。因此,大括号省略段落不适用。
因此,B z{.a = 1};必须失败。
其他编译器没有捕捉到这一点的原因可能如下。他们可能通过去除指定并在非连续元素之间插入任何默认成员初始值设定项/值初始化,然后应用正常聚合初始值设定项规则来实现指定初始值设定项。但这并不完全相同,因为指定的初始值设定项列表不参与大括号省略。