47 votes

Quand peut-on omettre des accolades externes dans une liste d'initialisation?

J'ai l'erreur C2078 dans VC2010 lors de la compilation le code ci-dessous.

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};

J'ai trouvé que la syntaxe correcte pour a1 est

std::array<A, 2> a1 = 
  {{
    {0, 0.1},
    {2, 3.4}
  }};

La question est: pourquoi supplémentaire accolades sont nécessaires pour a1 mais pas nécessaire pour a2?

Mise à jour

La question ne semble pas être spécifique à std::array. Quelques exemples:

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}}};

Je ne vois pas pourquoi struct D donne l'erreur, mais B et C ne le sont pas.

61voto

Nawaz Points 148870

Les extra-accolades est nécessaire parce que l' std::array est un agrégat et la GOUSSE d', contrairement à d'autres conteneurs de la bibliothèque standard. std::array n'ont pas défini par l'utilisateur constructeur. C'est le premier membre de données est un tableau de taille N (ce qui vous passe comme argument de modèle), et ce membre est directement initialisé avec l'initialiseur. Les extra-accolades est nécessaire à l' intérieur de la matrice de ce qui est directement en cours d'initialisation.

La situation est identique:

//define this aggregate - no user-defined constructor
struct Aarray
{
   A data[2];  //data is an internal array!
};

Comment initialiser ce? Si vous faites cela:

Aarray a1 =
{
   {0, 0.1},
   {2, 3.4}
};

il donne d'erreur de compilation:

erreur: trop de initialiseurs pour "Aarray'

C'est la même erreur que vous obtenez dans le cas d' std::array également (si vous utilisez GCC)!

Donc la bonne façon est d'utiliser des accolades comme:

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!
};

qui compile amende. Une fois de plus l'extra-accolades est nécessaire parce que vous êtes à l'initialisation de l' intérieur de la matrice.

--

Maintenant, la question est, pourquoi supplémentaire accolades ne sont pas nécessaires dans le cas d' double?

C'est parce qu' double n'est pas un agrégat, tout en A est. En d'autres termes, std::array<double, 2> est un agrégat de total, alors que std::array<A, 2> est un agrégé de agrégé de agrégé1.

1. Je pense que les extra-accolades est toujours nécessaire en cas de double aussi (comme cela), pour être complètement le Standard conforme, mais il fonctionne sans elle. Il semble que j'ai besoin de creuser la spec de nouveau!.

Plus sur les accolades et extra accolades

Je creuse la spec. Cette section (§8.5.1/11 à partir de C++11) est intéressant et s'applique à la présente affaire:

Dans une déclaration de la forme

T x = { a };

les accolades peuvent être gommés dans un initialiseur-liste comme suit. Si l'initialiseur-liste commence par une accolade gauche, puis le suivant liste séparée par des virgules de initialiseur-clauses initialise les membres d'une subaggregate; il est erroné pour qu'il y ait plus d'initialiseur-clauses de membres. Si, toutefois, l'initialiseur de liste pour un subaggregate ne commence pas par une accolade gauche, puis seulement assez d'initialiseur-les clauses de la liste sont prises pour initialiser les membres de la subaggregate; tout solde de l'initialiseur-des clauses de gauche pour initialiser le prochain membre de la somme dont l'actuel subaggregate est membre. [ Exemple:

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

est complètement contreventées l'initialisation: 1, 3, et 5 initialiser la première ligne du tableau y[0], à savoir l' y[0][0], y[0][1], et y[0][2]. De même, les deux lignes suivantes initialiser y[1] et y[2]. L'initialisation se termine tôt et, par conséquent, y[3]s des éléments sont initialisés comme si explicitement initialisée avec une expression de la forme float(), qui est initialisé avec 0.0. Dans l'exemple suivant, les accolades dans l'initialiseur-liste sont gommés; cependant l'initialiseur-liste a le même effet que l'complètement contreventées initialiseur-liste de l'exemple ci-dessus,

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

L'initialiseur, pour y commence par une accolade gauche, mais l'un pour l' y[0] n'a donc pas les trois éléments de la liste sont utilisés. De même, les trois sont pris successivement pour y[1] et y[2]. fin de l'exemple ]

Basé sur ce que j'ai compris de la citation ci-dessus, je peux dire que les éléments suivants devraient être autorisés:

//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}
}};

Dans le premier des accolades à l'intérieur-l'ensemble sont totalement gommés, tandis que le second est complètement contreventées d'initialisation. Dans votre cas (le cas de l' double), l'initialisation utilise la première approche (accolades sont totalement gommés pour l'agrégat interne!).

Mais ce doit être rejetée:

//ILL-FORMED : it is neither!
std::array<A, 2> Z = 
{
  {0, 0.1},
  {2, 3.4}
};

Il est ni bretelles-élidés, il n'existe pas suffisamment d'appareils pour être complètement contreventées d'initialisation. Par conséquent, il est mal formé!

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X