为什么指定的初始化被 gcc 接受而没有效果,并且生成的聚合以某种方式作为参数传递给 class 构造函数

Why is designated initialization accepted by gcc with no effect, and generated aggregate is somehow passed as arguments to class constructor

好的,我们有这个代码:

#include <iostream>
using namespace std;

class A{
    public:
    A(double b, int g) {
        cout << "b = " << b << ", g = " << g << endl;
    }
};

int main()
{
    A a = {.g = 5.0, .j = 10};      // output: b = 5, g = 10 
    A b = {.b = 5.0, .g = 10};      // output: b = 5, g = 10
    A bb = { 4.0, 20 };             // output: b = 4, g = 20
    A c = {.g = 5.0, .b = 10, 9};   // error: could not convert ‘{5.0e+0, 10, 9}’ from ‘<brace-enclosed initializer list>’ to ‘A’
    A d = {.g = 5, .b = 10.0};      // error: narrowing conversion of ‘1.0e+1’ from ‘double’ to ‘int’ inside { } [-Wnarrowing]
    A e = {.b = 10.0};              // error: could not convert ‘{1.0e+1}’ from ‘<brace-enclosed initializer list>’ to ‘A’
    return 0;
}

main() 的前三行编译,其他在注释中产生错误。 因此,实验表明:

  1. .name = 指定的初始化器没有作用。编译器简单地忽略任何命名字段
  2. 只有 {} 大括号中值的顺序很重要。
  3. 编译器生成 <brace-enclosed initializer list> 并查找 class A 的构造函数,其参数按类型和计数匹配列表中的元素,否则会产生错误。

使用 g++ 编译器 9.3。传递 -pedantic 标志会在 {} 大括号

中为每个带有指示符的值抛出警告

warning: C++ designated initializers only available with ‘-std=c++2a’ or ‘-std=gnu++2a’

传递 -std=c++2a 隐藏警告,并为 A c = {.g = 5.0, .b = 10, 9}; 提供另一条错误消息:

error: either all initializer clauses should be designated or none of them should be

clang 无法编译前两行,即使使用 -std=c++2a,也会为每一行生成

error: no matching constructor for initialization of 'A'
note: candidate constructor not viable: cannot convert argument of incomplete type 'void' to 'double' for 1st argument
note: candidate constructor (the implicit copy constructor) not viable: requires 1 argument, but 2 were provided
note: candidate constructor (the implicit move constructor) not viable: requires 1 argument, but 2 were provided

关于 Designated initializers in C++ 的 Cppreference 仅显示 class 字段的命名初始化情况,但不显示构造函数参数。聚合初始化和列表初始化示例也没有显示构造函数的此类功能。

好吧,如警告中所述,这种情况可能被视为 C++20 功能,但查看 C++ 20 features 以及建议功能的 cppreference.com 只给出了指定的初始化聚合类型,但不是通过构造函数参数,就像我的情况一样。

问题:

具有用户定义的构造函数的 class 不是聚合 class。如果使用指定的初始化程序,则 class 应为聚合。您的程序在非聚合上使用指定的初始化程序,因此它是一个格式错误的程序。允许编译器不编译它,并且需要诊断问题。

此外,指定的初始化程序应命名聚合的成员。您的程序没有在初始化程序中命名成员,因此它的格式不正确。

Is it ISO C++20 or GNU C++ feature?

指定初始化器通常是标准的 C++20 功能,也是 C++20 之前的 GNU 功能。

使用指定的初始化程序初始化非聚合不是标准的 C++ 功能。无论是错误还是功能,都是特定于实现的事情。

Should state 1. be treated as gcc compiler silent bug?

缺少诊断消息是违反标准的。

这似乎已在 trunk 中修复,所有使用指定初始化器的情况在 GCC 中都是一个错误:https://godbolt.org/z/TWKob6最新发布的版本 10.2 仍然重现了错误。