web-dev-qa-db-ja.com

初期化子リストで外括弧を省略できるのはいつですか?

以下のコードをコンパイルすると、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ではエラーが発生しません。

47
Andrey

標準ライブラリの他のコンテナとは異なり、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(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
};

which 正常にコンパイルされます 。繰り返しになりますが、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]を初期化します。同様に、次の2行はy[1]y[2]を初期化します。初期化子は早期に終了するため、y[3]s要素は、float()形式の式で明示的に初期化されたかのように初期化されます。つまり、0.0で初期化されます。次の例では、initializer-listの中括弧は省略されています。ただし、initializer-listには、上記の例の完全にブレースされたinitializer-listと同じ効果があります。

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

Yの初期化子は左中括弧で始まりますが、y[0]の初期化子はそうではないため、リストの3つの要素が使用されます。同様に、次の3つは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}
}};

最初のものでは、内部集合体のブレースが完全に削除され、2番目のブレースは完全にブレースされた初期化があります。あなたの場合(doubleの場合)、初期化は最初のアプローチを使用します(内部集合体では中括弧は完全に省略されます)。

しかし、これは許可されるべきではありません:

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

中括弧が省略されておらず、完全に中括弧で囲まれた初期化に十分な中括弧もありません。したがって、それは不正な形式です。

61
Nawaz