在std::array初始化中的花括号省略

48
假设有一个需要初始化的 std::array。如果使用双括号,那么这是可以的:
std::array<int, 2> x = {{0, 1}};
std::array<int, 2> x{{0, 1}};

在传统的聚合初始化中,使用单个大括号也可以,因为大括号省略会处理缺少的大括号:
std::array<int, 2> x = {0, 1};

然而,使用单花括号进行列表初始化是否可行?GCC可以接受,而Clang会拒绝,并显示“在使用直接列表初始化时,无法省略对子对象初始化周围的大括号”。
std::array<int, 2> x{0, 1};

标准中提到花括号省略的唯一部分是8.5.1/12,其内容如下:
所有隐式类型转换(第4条款)在使用赋值表达式初始化聚合成员时都会被考虑。如果赋值表达式可以初始化一个成员,则该成员将被初始化。否则,如果该成员本身是子聚合,则假定省略了花括号,并且将考虑赋值表达式来初始化子聚合的第一个成员。
8.5.1特指聚合初始化,因此这意味着Clang的拒绝是正确的,对吗?不要太快下结论。8.5.4/3说:
对象或类型T的引用的列表初始化定义如下:
[...]
- 否则,如果T是聚合体,则执行聚合初始化(8.5.1)。
我认为这意味着与聚合初始化完全相同的规则,包括花括号省略,适用于列表初始化,这意味着GCC的接受是正确的。
我承认,措辞并不特别清晰。那么,在第三个片段的处理中,哪个编译器是正确的?花括号省略是否发生在列表初始化中?

“类赋值初始化”被称为拷贝初始化。它调用的是复制构造函数,而不是赋值运算符。 - TemplateRex
1
最好使用标准术语,否则人们可能会感到困惑,并且可能会出现像这样的对话 :-) - TemplateRex
不是要卖弄学问,只是要准确,因为在SO上有很多关于赋值/复制混淆的问题。 - TemplateRex
如果使用双括号,那也没关系。但据我所知,标准并没有保证它能与双括号一起正常工作。 - Johannes Schaub - litb
在 Stroustrup 的《The C++ programming language》的 C++11 版本中,他在简化的 Array 示例(8.2.4,第208页)中使用了直接列表初始化方式,但是最后一个单词当然已经符合了 C++11 标准。 - maxschlepzig
显示剩余6条评论
2个回答

24

目前,即使使用-std=c++17选项,在archlinux中的clang-5.0仍不支持花括号省略。 - Victor Polevoy

11

相关内容:http://en.cppreference.com/w/cpp/language/aggregate_initialization

简而言之,

struct S {
    int x;
    struct Foo {
        int i;
        int j;
        int a[3];
    } b;
};
S s1 = { 1, { 2, 3, {4, 5, 6} } };
S s2 = { 1, 2, 3, 4, 5, 6}; // same, but with brace elision
S s3{1, {2, 3, {4, 5, 6} } }; // same, using direct-list-initialization syntax
S s4{1, 2, 3, 4, 5, 6}; // error in C++11: brace-elision only allowed with equals sign
                        // okay in C++14

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接