51

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

4

1 回答 1

66

需要额外的大括号,因为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}
};

它给出了编译错误

错误:“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
};

编译得很好。再一次,需要额外的大括号,因为您正在初始化内部数组。

--

现在的问题是为什么在 的情况下不需要额外的大括号double

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

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

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

我仔细研究了规范。本节(来自 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 的初始化程序以左大括号开头,但 fory[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}
};

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

于 2012-07-31T07:18:35.877 回答