简短版:以
{
开头的初始化器会停止花括号省略。在使用
{1,2}
的第一个例子中是这种情况,但在使用
A{1,2}
的第三个和第四个例子中则不是。花括号省略会消耗接下来的N个初始化器(N取决于要初始化的聚合),这就是为什么只有N的第一个初始化器不能以
{
开头。
在我知道的所有C++标准库实现中,
std::array
是一个包含C风格数组的结构体。也就是说,你有一个包含一个子聚合的聚合,就像…
template<typename T, std::size_t N>
struct array
{
T __arr[N];
};
当使用花括号初始化列表初始化std::array
时,您需要初始化包含的数组的成员。因此,在这些实现中,显式形式为:
std::array<A, 4> x = {{ {1,2}, {3,4}, {5,6}, {7,8} }};
最外层的大括号是指 std::array
结构体;第二层的大括号是指嵌套的 C 风格数组。
C++ 允许在聚合初始化时省略初始化嵌套聚合时的某些大括号。例如:
struct outer {
struct inner {
int i;
};
inner x;
};
outer e = { { 42 } };
outer o = { 42 };
规则如下(使用N4527草案,该草案是C++14之后的版本,但C++11中已经存在与此相关的缺陷):
花括号可以在初始化列表中省略。如果初始化列表以左花括号开头,则随后逗号分隔的初始化子句列表将初始化子聚合体的成员;如果初始化子句多于成员,则会出错。然而,如果子聚合体的初始化列表不以左花括号开头,则只有足够的初始化子句被用来初始化子聚合体的成员;任何剩余的初始化子句都将留给当前子聚合体是其成员的聚合体的下一个成员进行初始化。
将此应用于第一个std::array示例:
static std::array<A, 4> x1 =
{
{ 1, 2 },
{ 3, 4 },
{ 5, 6 },
{ 7, 8 }
};
这段话的意思如下:
如下所示:
static std::array<A, 4> x1 =
{
{
1,
2
}
{3,4},
{5,6},
...
};
第一个{
被视为std::array
结构的初始化器。然后,initializer-clauses{1,2},{3,4}
等被视为std::array
子聚合的初始值设定项。请注意,std::array
仅有一个子聚合__arr
。由于第一个initializer-clause {1,2}
以{
开头,因此不会发生brace-elision exception,编译器尝试使用{1,2}
来初始化嵌套的A __arr [4]
数组。剩余的initializer-clauses{3,4},{5,6}
等不涉及std::array
的任何子聚合,因此是非法的。
在第三个和第四个示例中,std::array
子聚合的第一个initializer-clause 不以{
开头,因此应用了大括号省略例外。
static std::array<A, 4> x4 =
{
A{ 1, 2 },
{ 3, 4 },
{ 5, 6 },
{ 7, 8 }
};
因此,它的解释如下:
static std::array<A, 4> x4 =
{ // x4 {
// __arr { -- brace elided
A{ 1, 2 }, // __arr[0]
{ 3, 4 }, // __arr[1]
{ 5, 6 }, // __arr[2]
{ 7, 8 } // __arr[3]
// } -- brace elided
}; // }
因此,
A{1,2}
会消耗所有四个
初始化语句 来初始化嵌套的 C 风格数组。如果您添加另一个初始化语句:
static std::array<A, 4> x4 =
{
A{ 1, 2 },
{ 3, 4 },
{ 5, 6 },
{ 7, 8 },
X
};
然后这个X
会用来初始化下一个std::array
的子聚合体。例如:
struct outer {
struct inner {
int a;
int b;
};
inner i;
int c;
};
outer o =
{
1,
2,
3
};
花括号省略规则会消耗接下来的N个初始化子句,其中N是通过需要进行初始化的(子)聚合体所需的初始化器数量来定义的。因此,它只关注接下来的N个初始化子句中的第一个是否以{
起始。
更类似于原帖:
struct inner {
int a;
int b;
};
struct outer {
struct middle {
inner i;
};
middle m;
int c;
};
outer o =
{
inner{1,2},
3
};
请注意,花括号省略是递归应用的; 我们甚至可以编写令人困惑的代码。
outer o =
{
1,
2,
3
};
当我们省略调用 o.m 和 o.m.i 的两个大括号时,前两个初始化子句被用于初始化 o.m.i,剩下的一个初始化 o.c。一旦我们在“1,2”的周围插入一对大括号,它就被解释为与 o.m 对应的一对大括号:
outer o =
{
{
1,
2,
}
3
};
在这里,o.m
的初始化程序以{
开头,因此不适用花括号省略。 o.m.i
的初始化程序为1
,它不以{
开头,因此对于o.m.i
应用花括号省略,并使用两个初始化程序1
和2
。