61

Possible Duplicate:
c++ why initializer_list behavior for std::vector and std::array are different

I defined simple 2D array (3X2):

  std::array<std::array<int,3>,2> a {
    {1,2,3},
    {4,5,6}
  };

I was surprised this initialization does not work, with gcc4.5 error: too many initializers for 'std::array<std::array<int, 3u>, 2u>'

Why can't I use this syntax?

I found workarounds, one very funny with extra braces, but just wonder why the first, easiest approach is not valid?

Workarounds:

  // EXTRA BRACES
  std::array<std::array<int,3>,2> a {{
    {1,2,3},
    {4,5,6}
  }};

  // EXPLICIT CASTING
  std::array<std::array<int,3>,2> a {
    std::array<int,3>{1,2,3},
    std::array<int,3>{4,5,6}
  };

[UPDATE]

Ok, thanks to KerrekSB and comments I get the difference. So it seems that there is too little braces in my example, like in this C example:

struct B {
  int array[3];
};
struct A {
  B array[2];
};

B b = {{1,2,3}};
A a = {{
     {{1,2,3}},
     {{4,5,6}}
}};
Community
  • 1
  • 1
PiotrNycz
  • 23,099
  • 7
  • 66
  • 112
  • `std::array` is an aggregate. – chris Oct 11 '12 at 16:42
  • I would also expect this to work. BTW another workaround is to omit the inner braces, although it produces warnings on gcc 4.8. – juanchopanza Oct 11 '12 at 16:47
  • 1
    The multidimensional case isn't different from the single-dimensional case, though compiler support may vary. `std::array a{1,2};` is ill-formed as well (gcc 4.7.2 will incorrectly accept such code; clang 3.1 will not). See the duplicate to which I linked above. The short answer is: this is a known defect in the C++11 language standard. – James McNellis Oct 11 '12 at 16:49

1 Answers1

80

std::array<T, N> is an aggregate that contains a C array. To initialize it, you need outer braces for the class itself and inner braces for the C array:

std::array<int, 3> a1 = { { 1, 2, 3 } };

Applying this logic to a 2D array gives this:

std::array<std::array<int, 3>, 2> a2 { { { {1, 2, 3} }, { { 4, 5, 6} } } };
//                                   ^ ^ ^ ^            ^ ^
//                                   | | | |            | |
//                                   | +-|-+------------|-+
//                                   +-|-+-|------------+---- C++ class braces
//                                     |   |
//                                     +---+--- member C array braces
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • 6
    But this works fine: `std::array a1 { 1, 2, 3 };` ? – PiotrNycz Oct 11 '12 at 16:56
  • 5
    @PiotrNycz: Only if you're very sloppy and ignore all warnings. My compiler says, `warning: missing braces around initialiser for ‘std::array::value_type [3] {aka int [3]}’ [-Wmissing-braces]`. – Kerrek SB Oct 11 '12 at 17:00
  • I get it now, thanks. I added to my question example with C. – PiotrNycz Oct 11 '12 at 17:03
  • @PiotrNycz My reading of the standard is that `std::array` is not required to be implemented as a single element aggregate. I interpret §23.3.2.1 as stating that it can be initialized as per your first example. So I think the single brace syntax if fine. – juanchopanza Oct 11 '12 at 17:03
  • 1
    @juanchopanza James McNellis wrote in comments this is known bug in C++11, maybe we can use single braces some day... – PiotrNycz Oct 11 '12 at 17:08
  • @KerrekSB: Actually that is sanctioned by the standard as valid (it is called *brace-elision*), and there is even an example. I don't think that you should call *sloppy* someone that does exactly the same as the standard (8.5.1p12 is the particular example). – David Rodríguez - dribeas Oct 11 '12 at 17:11
  • @DavidRodríguez-dribeas: Fair enough. Does the brace elision nest arbitrarily? – Kerrek SB Oct 11 '12 at 17:13
  • @PiotrNycz as I said in another comment, you could completely eliminete the inner braces, and your example would probably compile (it does on my gcc 4.8 as well as on 4.6.3. In this case, brace elision kicks in. – juanchopanza Oct 11 '12 at 17:13
  • @juanchopanza - yes it works - thanks. However still some different viewpoints in comments on this. I mean this works: `a = {1,2,3,4,5,6}`. – PiotrNycz Oct 11 '12 at 17:28
  • @KerrekSB: Not really sure. My understanding is that it does, but I am not too familiar with the gory details of aggregate initialization (and to be honest I tend to write all braces myself, I have never encountered the nested `std::array` or any other case where so many unneded braces were needed) – David Rodríguez - dribeas Oct 11 '12 at 19:01
  • @DavidRodríguez-dribeas: Well, I know that you can omit braces for multidimensional, naked C arrays, whose initializer you can write as a flat list. But I didn't know that this was generally also allowed for arbitrary aggregates... I'll check the standard again. – Kerrek SB Oct 12 '12 at 18:29
  • @KerrekSB: I did check yesterday. It is allowed by the standard if the initialization is *copy-initialization* (`T a = {}`), and explicitly disallowed if it is *direct-initialization* (`T a{}`). There is a defect report filled to lift that restriction and allow brace-elision to occur with both syntaxes. I should have added this comment yesterday when I found out. – David Rodríguez - dribeas Oct 12 '12 at 18:35
  • This is a great comment. I had always wondered why we needed the double braces but hadn't put that much thought into it. It makes much more sense now. – Sebastian Jan 09 '23 at 16:33