初始化列表中什么时候可以省略外大括号?

问题描述 投票:0回答:1

我在VC2010中编译下面的代码时遇到错误C2078。

struct A
  {
  int foo;
  double bar;
  };

std::array<A, 2> a1 = 
  // error C2078: too many initializers
  {
    {0, 0.1},
    {2, 3.4}
  };

// OK
std::array<double, 2> a2 = {0.1, 2.3};

我发现

a1
的正确语法是

std::array<A, 2> a1 = 
  {{
    {0, 0.1},
    {2, 3.4}
  }};

问题是:为什么

a1
需要额外的大括号,而
a2
不需要额外的大括号?

更新

这个问题似乎并不特定于 std::array。一些例子:

struct B
  {
  int foo[2];
  };

// OK
B meow1 = {1,2};
B bark1 = {{1,2}};

struct C
  {
  struct 
    { 
    int a, b; 
    } foo;
  };

// OK
C meow2 = {1,2};
C bark2 = {{1,2}};

struct D
  {
  struct 
    { 
    int a, b; 
    } foo[2];
  };

D meow3 = {{1,2},{3,4}};  // error C2078: too many initializers
D bark3 = {{{1,2},{3,4}}};

我仍然不明白为什么

struct D
给出了错误,但 B 和 C 却没有。

c++ c++11 visual-c++-2010
1个回答
70
投票

需要额外的大括号,因为

std::array
是一个聚合和普通旧数据类型(POD),与标准库中的其他容器不同。
std::array
没有用户定义的构造函数。它的第一个数据成员是一个大小为
N
的数组(作为模板参数传递),并且该成员直接使用初始值设定项进行初始化。直接初始化的 internal 数组需要额外的大括号。

情况相同:

//define this aggregate - no user-defined constructor
struct Aarray
{
   A data[2];  //data is an internal array
};

你将如何初始化它?如果你这样做:

Aarray a1 =
{
   {0, 0.1},
   {2, 3.4}
};

它给出了编译错误

错误:“Aarray”的初始化器太多

这是在 std::array 情况下遇到的

same
错误(如果您使用 GCC)。

所以正确的做法是使用大括号,如下所示:

Aarray a1 =
{
  {  //<--this tells the compiler that initialization of `data` starts

        { //<-- initialization of `data[0]` starts

           0, 0.1

        }, //<-- initialization of `data[0]` ends

       {2, 3.4}  //initialization of data[1] starts and ends, as above

  } //<--this tells the compiler that initialization of `data` ends
};

编译良好。再次需要额外的大括号,因为您正在初始化 internal 数组。

--

现在的问题是,为什么在

double
的情况下不需要额外的支架?

这是因为

double
不是聚合,而
A
是聚合。换句话说,
std::array<double, 2>
是聚合的聚合,而
std::array<A, 2>
是聚合1的聚合的聚合。

1。我认为在 double 的情况下仍然需要额外的大括号(如 this),以完全符合标准,但代码无需它们即可工作。看来我需要再次深入研究规范!

#更多关于牙套和额外牙套的信息

我仔细研究了规范。本节(来自 C++11 的§8.5.1/11)很有趣并且适用于这种情况:

在表格声明中

T x = { a };

可以在初始化列表中省略大括号,如下所示。如果初始化器列表以左大括号开头,则后续的以逗号分隔的初始化器子句列表将初始化子聚合的成员;初始化子句的数量多于成员的数量是错误的。但是,如果子聚合的初始值设定项列表 不以左大括号开头,则仅从列表中获取足够的初始化子句来初始化子聚合的成员;任何剩余的初始化子句都用于初始化当前子聚合所属的聚合的下一个成员。 [ 示例:

float y[4][3] = {
{ 1, 3, 5 },
{ 2, 4, 6 },
{ 3, 5, 7 },
};

是完全支撑的初始化:1、3和5初始化数组

y[0]
的第一行,即
y[0][0]
y[0][1]
y[0][2]
。同样,接下来的两行初始化
y[1]
y[2]
。初始化器提前结束,因此
y[3]s
元素被初始化,就像用 float() 形式的表达式显式初始化一样,即用 0.0 初始化。在下面的示例中,初始化列表中的大括号被省略;然而初始化器列表与上面示例的完全支撑初始化器列表具有相同的效果,

float y[4][3] = {
1, 3, 5, 2, 4, 6, 3, 5, 7
};

y 的初始化器以左大括号开头,但

y[0]
的初始化器则不然,因此使用列表中的三个元素。同样,对于
y[1]
y[2]
,连续取接下来的三个。 —示例结束]

根据我对上述引用的理解,我可以说应该允许以下内容:

//OKAY. Braces are completely elided for the inner-aggregate
std::array<A, 2> X =   
{
  0, 0.1,
  2, 3.4
};

//OKAY. Completely-braced initialization
std::array<A, 2> Y = 
{{
   {0, 0.1},
   {2, 3.4}
}};

在第一个中,内部聚合的大括号被完全省略,而第二个则具有完全支撑的初始化。在您的情况下(

double
的情况),初始化使用第一种方法(对于内部聚合,大括号被完全省略)。

但这应该是不允许的:

//ILL-FORMED : neither braces-elided, nor fully-braced
std::array<A, 2> Z = 
{
  {0, 0.1},
  {2, 3.4}
};

它既没有省略大括号,也没有足够的大括号来进行完全支撑初始化。因此,它的格式不正确。

© www.soinside.com 2019 - 2024. All rights reserved.