2

In the following program, I tried to initialise my 2x2-element std::array using the line which has nested list initialisation (currently commented out). MSVC2017 gave me a compiler error with "too many initializers". Why?

I then gave the non-nested list initialisation a go which worked. Why?

This appears to be inconsistent with initialisation of a nested std::vector. See third line of main(). What is going on here please?

#include <array>
#include <iostream>
#include <vector>

int main()
{
    //std::array<std::array<int,2>,2> x = {{0,1},{2,3}};    // ERROR: TOO MANY INITIALIZERS
    std::array<std::array<int,2>,2> x = {0,1,2,3};          // OK
    std::vector<std::vector<int>> y = {{0,1},{2,3}};        // ALSO OK: NOT TOO MANY INITIALIZERS IF A std::vector?
}
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
PeteUK
  • 1,062
  • 12
  • 26
  • Does this your answer? https://stackoverflow.com/questions/12844475/why-cant-simple-initialize-with-braces-2d-stdarray – Behnam Arazkhani Mar 09 '20 at 11:14
  • Thanks guys. I did search beforehand but not very well it seems. Yes, my question is a duplicate. Should I delete the question now? – PeteUK Mar 09 '20 at 11:28
  • 1
    @PeteUK You don't have to. Duplicate questions are helpful when other users' search finds your question but not the dupe target. – L. F. Mar 09 '20 at 11:57

2 Answers2

1

std::array is a little special. It's usually implemented as a wrapper of built-in arrays. Then for aggregate initialization, you need to add another pair of {}, as

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

On the other hand, std::array<std::array<int,2>,2> x = {0,1,2,3}; works because of brace elision.

If the aggregate initialization uses copy-list-initialization syntax (T a = {args..}), (until C++14)the braces around the nested initializer lists may be elided (omitted), in which case as many initializer clauses as necessary are used to initialize every member or element of the corresponding subaggregate, and the subsequent initializer clauses are used to initialize the following members of the object.

If you specified nested initializer list, you have to specify the initializer list in the right layers. For this case, it should be 3 layers.

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
1

In this declaration

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

you have three nested aggregates. The first pair of values enclosed in braces

{0,1}

is considered by the compiler as an initializer of the second aggregate that is present in the declaration as one sub-aggregate. So the second pair of values in braces

{2,3}

are considered by the compiler as redundant that has no corresponding object.

You could declare the array for example like

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

The braces may be elided when an aggregate is initialized. (C++17 Standard, 11.6.1 Aggregates)

12 Braces can be elided in an initializer-list as follows. If the initializer-list begins with a left brace, then the succeeding comma-separated list of initializer-clauses initializes the elements of a subaggregate; it is erroneous for there to be more initializer-clauses than elements. If, however, the initializer-list for a subaggregate does not begin with a left brace, then only enough initializer-clauses from the list are taken to initialize the elements of the subaggregate; any remaining initializer-clauses are left to initialize the next element of the aggregate of which the current subaggregate is an element.

So in this declaration

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

the braces are elided and the aggregate is initialized as it is described in the quote..

In this declaration

std::vector<std::vector<int>> y = {{0,1},{2,3}};

there is used the constructor of the class std::vector that accepts std::initializer_list as an argument. In this case the constructor builds as many elements of the vector as there are elements in the initializer list.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335