标准是否使用多余的大括号(例如 T{{{10}}})定义列表初始化?

Does the standard define list initialization with superfluous braces (e.g. T{{{10}}})?

本文关键字:定义 初始化 列表 例如 是否 多余 标准      更新时间:2023-10-16

当使用包含多个大括号初始化列表的大括号初始化列表时,标准为 B、C 和 D 定义的规则是什么?

对于 B,我相信这种情况在标准中被定义为具有单个元素的大括号 init-list,因此它直接调用Test(int),没有临时 - 但我找不到在哪里。

对于 C 和 D,我不确定这是否是未定义的行为。

我也对使用多个元素时会发生什么感兴趣,即{{{1, 2}}},如果这会改变 B、C 或 D 的行为?

#include <iostream>
struct Test {
Test(const int a) {
// A and B call this
}
Test(Test&& test) = delete;
Test(const Test& test) = delete;
};
int main()
{
Test a{1}; // calls Test(int)
Test b{{2}}; // B
Test c{{{3}}}; // C
Test d{{{{4}}}}; // D
// Test e{a}; error, deleted copy constructor
// Test f{Test{0}}; error, deleted move constructor
return 0;
}

GCCg++ my_prog.cpp只给了我一个 C 和 D 的错误:

my_prog.cpp: In function 'int main()':
my_prog.cpp:16:17: error: too many braces around initializer for 'int' [-fpermissive]
Test c{{{3}}};
^
my_prog.cpp:4:14: note:   initializing argument 1 of 'Test::Test(int)'
Test(int a) {
~~~~^

当你有

Test b{{2}}; 

[dcl.init.list]/3.7 状态。

否则,如果 T 是类类型,则考虑构造函数。枚举适用的构造函数,并通过重载解析([over.match]、[over.match.list])选择最佳构造函数。[...]

在 [over.match] 中,我们有 [over.match.ctor]/1

当类类型的对象是直接初始化、从相同表达式或派生类类型 ([dcl.init]) 复制初始化或默认初始化时,重载解析将选择构造函数。对于不在复制初始化上下文中的直接初始化或默认初始化,候选函数都是要初始化的对象类的所有构造函数。对于复制初始化(包括复制初始化上下文中的默认初始化),候选函数是该类的所有转换构造函数 ([class.conv.ctor])。参数列表是初始值设定项的表达式列表或赋值表达式。

所以我们考虑构造函数,找到

Test(const int a)

然后我们使用元素{2}作为使用 [dcl.init.list]/3.9 的a的初始值设定项

否则,如果初始值设定项列表具有类型 E 的单个元素,并且 T 不是引用类型或其引用类型与 E 相关,则对象或引用将从该元素初始化(通过复制初始化进行复制列表初始化,或通过直接初始化直接初始化直接列表初始化);如果需要缩小转换(见下文)才能将元素转换为 T, 程序格式不正确。

Test c{{{3}}};
// and
Test d{{{{4}}}};

我们做同样的事情。 我们查看构造函数并找到

Test(const int a)

作为唯一可行的。 当我们这样做并尝试初始化a时,我们再次查看 [dcl.init.list]/3.9,但它在这里不适用。{{3}}{{{4}}}不是具有单一类型E的初始值设定项列表。大括号的 init-list没有类型,所以我们必须继续在 [dcl.init.list]/3 中使用列表。 当我们这样做时,我们不会遇到任何其他匹配的东西,直到 [dcl.init.list]/3.12

否则,程序格式不正确。