何时可以在初始化列表中省略外括号?

时间:2012-07-31 07:06:02

标签: c++ c++11 visual-c++-2010

编译下面的代码时,我在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没有。

1 个答案:

答案 0 :(得分:60)

需要额外的大括号,因为std::array是聚合和POD,与标准库中的其他容器不同。 std::array没有用户定义的构造函数。它的第一个数据成员是一个大小为N的数组(您将其作为模板参数传递),并使用初始化程序直接初始化此成员。 内部数组需要额外的大括号,直接初始化。

情况与:

相同
//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}
};

它提供compilation error

  

错误:“Aarray”的初始化程序太多了

这是相同的错误,如果您使用GCC,则会得到错误。

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

std::array

compiles fine。再一次,需要额外的大括号,因为你正在初始化内部数组。

-

现在问题是为什么在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 }; 的情况下不需要额外的括号?

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

<击> 1。我认为在双倍的情况下仍然需要额外的括号(如this),以完全符合标准,但代码在没有它们的情况下工作。我似乎需要再次深入了解规范!

有关牙套和额外牙套的更多信息

我挖掘了规范。本节(来自C ++ 11的第8.5.1 / 11节)很有意思,适用于这种情况:

  

在表格

的声明中
std::array<A, 2>
  

大括号可以在初始化列表中删除,如下所示。如果初始化列表以左括号开头,则后续的逗号分隔的initializer-clause列表初始化子集合的成员;如果有比成员更多的初始化子条款是错误的。但是,如果是subaggregate的初始化列表   不以左括号开头,然后只从列表中获取足够的初始化子句来初始化子集合的成员;剩下的任何初始化子句用于初始化当前子聚合为成员的聚合的下一个成员。 [例如:

T x = { a };
  

是一个完全支撑的初始化:1,3和5初始化数组float y[4][3] = { { 1, 3, 5 }, { 2, 4, 6 }, { 3, 5, 7 }, }; 的第一行,即y[0]y[0][0]y[0][1]。同样,接下来的两行初始化y[0][2]y[1]。初始化程序提前结束,因此初始化y[2]元素,就像使用float()形式的表达式显式初始化一样,即初始化为0.0。在以下示例中,初始化列表中的大括号被省略;但是初始化列表与上面示例的完全支撑的初始化列表具有相同的效果,

y[3]s
  

y的初始化程序以左括号开头,但float y[4][3] = { 1, 3, 5, 2, 4, 6, 3, 5, 7 }; 的初始化程序没有,因此使用列表中的三个元素。同样,接下来的三个是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

它既没有括号,也没有足够的括号可以完全支撑初始化。因此,它是不正确的。