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

And*_*rey 47 c++ visual-c++-2010 c++11

编译下面的代码时,我在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};
Run Code Online (Sandbox Code Playgroud)

我发现正确的语法a1

std::array<A, 2> a1 = 
  {{
    {0, 0.1},
    {2, 3.4}
  }};
Run Code Online (Sandbox Code Playgroud)

问题是:为什么需要额外的括号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}}};
Run Code Online (Sandbox Code Playgroud)

我仍然不明白为什么struct D会给出错误,但B和C不会.

Naw*_*waz 61

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

情况如下:

//define this aggregate - no user-defined constructor
struct Aarray
{
   A data[2];  //data is an internal array
};
Run Code Online (Sandbox Code Playgroud)

你会如何初始化这个?如果你这样做:

Aarray a1 =
{
   {0, 0.1},
   {2, 3.4}
};
Run Code Online (Sandbox Code Playgroud)

它给出了编译错误:

错误:"Aarray"的初始化程序太多

这是 a std::array(如果使用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
};
Run Code Online (Sandbox Code Playgroud)

编译罚款.再次,需要额外的括号,因为您正在初始化内部数组.

-

现在的问题是为什么不需要额外的牙套double

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

1.我认为在双倍的情况下仍然需要额外的括号(像这样),完全符合标准,但代码在没有它们的情况下工作.看来我需要再次挖掘规范!.

更多关于牙套和额外牙套

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

在表格的声明中

T x = { a };
Run Code Online (Sandbox Code Playgroud)

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

float y[4][3] = {
{ 1, 3, 5 },
{ 2, 4, 6 },
{ 3, 5, 7 },
};
Run Code Online (Sandbox Code Playgroud)

是一个完全-支撑初始化:1,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
};
Run Code Online (Sandbox Code Playgroud)

y的初始化程序以左括号开头,但是for的初始化程序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}
}};
Run Code Online (Sandbox Code Playgroud)

在第一个中,内部聚合的大括号完全被省略,而第二个则完全支持初始化.在您的情况下(例如double),初始化使用第一种方法(内部聚合完全省略了大括号).

但这应该是不允许的:

//ILL-FORMED : neither braces-elided, nor fully-braced
std::array<A, 2> Z = 
{
  {0, 0.1},
  {2, 3.4}
};
Run Code Online (Sandbox Code Playgroud)

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

  • 对于那些没有点击@juanchopanza链接的人,可以找到[官方缺陷报告]的链接(http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2012/n3367.html#1270关于支撑省略限制. (3认同)