为什么指定的初始化被 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() 的前三行编译,其他在注释中产生错误。
因此,实验表明:
.name =
指定的初始化器没有作用。编译器简单地忽略任何命名字段
- 只有 {} 大括号中值的顺序很重要。
- 编译器生成
<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 只给出了指定的初始化聚合类型,但不是通过构造函数参数,就像我的情况一样。
问题:
- 为什么以及如何工作?
- 它是 ISO C++20 还是 GNU C++ 功能?
- 是否应将状态 1. 视为 gcc 编译器静默错误?
具有用户定义的构造函数的 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 仍然重现了错误。
好的,我们有这个代码:
#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() 的前三行编译,其他在注释中产生错误。 因此,实验表明:
.name =
指定的初始化器没有作用。编译器简单地忽略任何命名字段- 只有 {} 大括号中值的顺序很重要。
- 编译器生成
<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 只给出了指定的初始化聚合类型,但不是通过构造函数参数,就像我的情况一样。
问题:
- 为什么以及如何工作?
- 它是 ISO C++20 还是 GNU C++ 功能?
- 是否应将状态 1. 视为 gcc 编译器静默错误?
具有用户定义的构造函数的 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 仍然重现了错误。