std :: array<>的初始化 [英] Initialisation of std::array<>
问题描述
考虑以下代码:
#include <array>
struct A
{
int a;
int b;
};
static std::array<A, 4> x1 =
{
{ 1, 2 },
{ 3, 4 },
{ 5, 6 },
{ 7, 8 }
};
static std::array<A, 4> x2 =
{
{
{ 1, 2 },
{ 3, 4 },
{ 5, 6 },
{ 7, 8 }
}
};
static std::array<A, 4> x3 =
{
A{ 1, 2 },
A{ 3, 4 },
A{ 5, 6 },
A{ 7, 8 }
};
static std::array<A, 4> x4 =
{
A{ 1, 2 },
{ 3, 4 },
{ 5, 6 },
{ 7, 8 }
};
使用gcc编译:
$ gcc -c --std=c++11 array.cpp
array.cpp:15:1: error: too many initializers for ‘std::array<A, 4ul>’
};
^
$
NB1:注释掉第一个初始化语句后,代码将正确编译.
NB2:将所有初始化都转换为构造函数调用会产生相同的结果.
NB3:MSVC2015的行为相同.
NB1: Commenting out the first initialisation statement, the code compiles without error.
NB2: Converting all the initialisation to constructor calls yields the same results.
NB3: MSVC2015 behaves the same.
我可以看到为什么第一次初始化无法编译,为什么第二次和第三次都可以. (例如,参见 C ++ 11:正确的std :: array初始化吗?.)
I can see why the first initialisation fails to compile, and why the second and third are OK. (e.g. See C++11: Correct std::array initialization?.)
我的问题是:为什么最终初始化要精确编译?
My question is: Why exactly does the final initialisation compile?
推荐答案
简短版本:以{
开头的初始化子句停止大括号删除.在使用{1,2}
的第一个示例中就是这种情况,但是在使用A{1,2}
的第三个也不是第四个示例中就是这种情况.括号删除消耗了接下来的N个初始化器子句(其中N取决于要初始化的聚合),这就是为什么只有N个第一个初始化器子句不能以{
开头的原因.
Short version: An initializer-clause that starts with {
stops brace-elision. This is the case in the first example with {1,2}
, but not in the third nor fourth which use A{1,2}
. Brace-elision consumes the next N initializer-clauses (where N is dependent on the aggregate to be initialized), which is why only the first initializer-clause of the N must not begin with {
.
在我所知道的C ++标准库的所有实现中,std::array
是一个包含C样式数组的结构.也就是说,您有一个包含 sub-aggregate 的聚合,很像
In all implementations of the C++ Standard Library I know of, std::array
is a struct which contains a C-style array. That is, you have an aggregate which contains a sub-aggregate, much like
template<typename T, std::size_t N>
struct array
{
T __arr[N]; // don't access this directly!
};
从 braced-init-list 初始化std::array
时,因此必须初始化包含数组的成员.因此,在这些实现上,显式形式为:
When initializing a std::array
from a braced-init-list, you'll therefore have to initialize the members of the contained array. Therefore, on those implementations, the explicit form is:
std::array<A, 4> x = {{ {1,2}, {3,4}, {5,6}, {7,8} }};
最外面的大括号表示std::array
结构;第二组大括号是指嵌套的C样式数组.
The outermost set of braces refers to the std::array
struct; the second set of braces refers to the nested C-style array.
C ++允许在初始化嵌套聚合时在聚合初始化中省略某些花括号.例如:
C++ allows in aggregate initialization to omit certain braces when initializing nested aggregates. For example:
struct outer {
struct inner {
int i;
};
inner x;
};
outer e = { { 42 } }; // explicit braces
outer o = { 42 }; // with brace-elision
规则如下(使用N4527之后的草案,即C ++ 14之后的版本,但C ++ 11仍然存在与此相关的缺陷):
The rules are as follows (using a post-N4527 draft, which is post-C++14, but C++11 contained a defect related to this anyway):
可以将括号隐藏在 initializer-list 中,如下所示.如果 initializer-list 以左大括号开头,然后是左大括号 以逗号分隔的 initializer-clauses 列表初始化成员 子集合;有更多的东西是错误的 initializer-clauses 而不是成员.但是,如果 initializer-list 因为子集合不是以左大括号开头,而是 从列表中提取足够的 initializer-clauses 来初始化 子集合的成员;其余的初始化器子句是 向左初始化集合中的下一个成员 当前的子集合是成员.
Braces can be elided in an initializer-list as follows. If the initializer-list begins with a left brace, then the succeeding comma-separated list of initializer-clauses initializes the members of a subaggregate; it is erroneous for there to be more initializer-clauses than members. If, however, the initializer-list for a subaggregate does not begin with a left brace, then only enough initializer-clauses from the list are taken to initialize the members of the subaggregate; any remaining initializer-clauses are left to initialize the next member of the aggregate of which the current subaggregate is a member.
将其应用于第一个std::array
-示例:
Applying this to the first std::array
-example:
static std::array<A, 4> x1 =
{
{ 1, 2 },
{ 3, 4 },
{ 5, 6 },
{ 7, 8 }
};
这解释如下:
static std::array<A, 4> x1 =
{ // x1 {
{ // __arr {
1, // __arr[0]
2 // __arr[1]
// __arr[2] = {}
// __arr[3] = {}
} // }
{3,4}, // ??
{5,6}, // ??
...
}; // }
第一个{
被用作std::array
结构的初始化程序.然后,将初始化程序子句 {1,2}, {3,4}
等用作std::array
子聚合的初始化程序.请注意,std::array
仅具有单个子聚合__arr
.由于第一个 initializer-clause {1,2}
以{
开头,因此不会出现 brace-elision异常,并且编译器会尝试初始化嵌套的A __arr[4]
{1,2}
组成的数组.其余的初始化程序子句 {3,4}, {5,6}
等均未引用std::array
的任何子集合,因此是非法的.
The first {
is taken as the initializer of the std::array
struct. The initializer-clauses {1,2}, {3,4}
etc. then are taken as the initializers of the subaggregates of std::array
. Note that std::array
only has a single subaggregate __arr
. Since the first initializer-clause {1,2}
begins with a {
, the brace-elision exception does not occur, and the compiler tries to initialize the nested A __arr[4]
array with {1,2}
. The remaining initializer-clauses {3,4}, {5,6}
etc. do not refer to any subaggregate of std::array
and are therefore illegal.
在第三个和第四个示例中,std::array
的子集合的第一个 initializer-clause 并非以{
开头,因此大括号省略例外已应用:
In the third and fourth example, the first initializer-clause for the subaggregate of std::array
does not begin with a {
, therefore the brace elision exception is applied:
static std::array<A, 4> x4 =
{
A{ 1, 2 }, // does not begin with {
{ 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}
使所有四个 initializer-clauses 被消耗,以初始化嵌套的C样式数组.如果添加另一个初始化程序:
Hence, the A{1,2}
causes all four initializer-clauses to be consumed to initialize the nested C-style array. If you add another initializer:
static std::array<A, 4> x4 =
{
A{ 1, 2 }, // does not begin with {
{ 3, 4 },
{ 5, 6 },
{ 7, 8 },
X
};
,则此X
将用于初始化std::array
的下一个子聚合.例如
then this X
would be used to initialize the next subaggregate of std::array
. E.g.
struct outer {
struct inner {
int a;
int b;
};
inner i;
int c;
};
outer o =
{ // o {
// i {
1, // a
2, // b
// }
3 // c
}; // }
括号删除将使用接下来的N个初始化程序子句,其中N是通过(子)集合初始化所需的初始化程序数来定义的.因此,仅重要的是,这N个初始化子句中的第一个是否以{
开头.
Brace-elision consumes the next N initializer-clauses, where N is defined via the number of initializers required for the (sub)aggregate to be initialized. Therefore, it only matters whether or not the first of those N initializer-clauses starts with a {
.
与OP更相似:
struct inner {
int a;
int b;
};
struct outer {
struct middle {
inner i;
};
middle m;
int c;
};
outer o =
{ // o {
// m {
inner{1,2}, // i
// }
3 // c
}; // }
请注意,括号删除是递归应用的;我们甚至可以写出令人困惑的
Note that brace-elision applies recursively; we can even write the confusing
outer o =
{ // o {
// m {
// i {
1, // a
2, // b
// }
// }
3 // c
}; // }
在这里我们省略了o.m
和o.m.i
的两个括号.前两个初始化器子句用于初始化o.m.i
,其余两个子句用于初始化o.c
.一旦我们在1,2
周围插入一对大括号,它将被解释为与o.m
对应的一对大括号:
Where we omit both the braces for o.m
and o.m.i
. The first two initializer-clauses are consumed to initialize o.m.i
, the remaining one initializes o.c
. Once we insert a pair of braces around 1,2
, it is interpreted as the pair of braces corresponding to o.m
:
outer o =
{ // o {
{ // m {
// i {
1, // a
2, // b
// }
} // }
3 // c
}; // }
在这里,o.m
的初始化程序的确以{
开头,因此大括号省略不适用. o.m.i
的初始值设定项是1
,它不是以{
开头,因此对o.m.i
应用大括号删除,并且消耗了两个初始值设定项1
和2
.
Here, the initializer for o.m
does start with a {
, hence brace-elision does not apply. The initializer for o.m.i
is 1
, which does not start with a {
, hence brace-elision is applied for o.m.i
and the two initializers 1
and 2
are consumed.
这篇关于std :: array<>的初始化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!