web-dev-qa-db-fra.com

Pourquoi l'initialisation d'un tableau de paires nécessite-t-elle toujours des doubles accolades en C++ 14?

Avec le standard C++ 14, l'initialisation d'un std::array peut aller avec des accolades simples (voir http://fr.cppreference.com/w/cpp/container/array ):

Ceci, cependant, ne fonctionne pas pour un std::array de std::pair

Pourquoi ces travaux:

std::pair<int, int> p { 1, 2 };
std::array<int, 3> a {1, 2, 3};

mais est-ce que not ne fonctionne pas:

std::array<std::pair<int, int>, 3> b {{1, 11}, {2, 22}, {3, 33}};

alors que cela fonctionne à nouveau?

std::array<std::pair<int, int>, 3> b {{{1, 11}, {2, 22}, {3, 33}}};

De plus, l’initialisation d’un bon vieux tableau fonctionne avec des accolades simples.

std::pair<int, int> c[3] {{1, 11}, {2, 22}, {3, 33}};
58
Chiel

Cela semble être une ambiguïté d'analyse un peu similaire à la célèbre analyse la plus vexante . Je soupçonne que ce qui se passe est que:

Si vous écrivez

std::array<std::pair<int, int>, 3> b {{1, 11}, {2, 22}, {3, 33}};

le compilateur a deux façons d'interpréter la syntaxe:

  1. Vous effectuez une initialisation complète (ce qui signifie que l’accolade la plus externe fait référence à l’initialisation globale du std::array, tandis que la première, la plus interne, initialise la représentation du membre interne de std::array qui est un véritable C-Array). La compilation échouera, car std::pair<int, int> ne pourra plus être initialisé par 1 (toutes les accolades sont épuisées). Clang donnera une erreur de compilation indiquant exactement que:

    error: no viable conversion from 'int' to 'std::pair<int, int>'
     std::array<std::pair<int, int>, 3> a{{1, 11}, {2, 22}, {3, 33}};
                                              ^
    

    Notez également que ce problème est résolu s'il n'y a pas d'agrégat de membre interne à initialiser, c'est-à-dire.

    std::pair<int, int> b[3] = {{1, 11}, {2, 22}, {3, 33}};
    

    compilera très bien l'initialisation globale.

  2. (Au sens où vous l'entendiez.) Vous effectuez une initialisation avec accolade. Les accolades les plus internes servent donc à l'initialisation d'agrégat des paires individuelles, tandis que les accolades des représentations de tableau internes sont supprimées. Notez que même s'il n'y avait pas cette ambiguïté, comme indiqué correctement dans la réponse de rustyx , les règles d'élision entre accolades ne s'appliquent pas, car std::pair n'est pas un type d'agrégat et le programme serait toujours mal formé.

Le compilateur préférera l'option 1. En fournissant les accolades supplémentaires, vous effectuez l'initialisation complète et éliminez toute ambiguïté syntaxique.

28
Jodocus

C++ 14 règle d'élimination d'accolade s'applique uniquement à l'initialisation sous-agrégée.

Ainsi, par exemple, quelque chose comme ceci fonctionne:

std::array<std::array<int, 3>, 3> a{1, 11, 2, 22, 3, 33};

Ici, un agrégat d'agrégats peut être initialisé par liste sans accolades supplémentaires.

Mais std::pair n'est pas un agrégat (il a des constructeurs), donc la règle ne s'applique pas.

Ce qui signifie que sans la règle d'élision d'accolades, std::array, lui-même constitué d'un agrégat avec un tableau, a besoin d'un ensemble supplémentaire d'accolades pour être list-initialisé. Rappelez-vous que le modèle de classe array est implémenté comme suit:

template<typename T, std::size_t N> 
struct array {
  T elems[N];
};

Pour list-initialize sans la règle d'élimination d'accolades, vous avez besoin d'un ensemble supplémentaire d'accolades pour accéder au membre elems.

14
rustyx

Sans les doubles accolades, la déclaration est simplement ambiguë. Considérons le code suivant:

    std::array<std::pair<int, int>, 1> a = {{ {1, 2} }};
    std::array<int, 2> b = { {1, 2} };

Sans doubles accolades dans la première définition, le compilateur considérera { {1,2} } comme une liste d'initialisation scalar pour array<int, 2>. Vous devez déclarer un nested braced-init-list explicite pour que le compilateur reconnaissez que la liste interne est aussi aggreg-initialized (vs scalar initialized), de sorte qu'elle puisse construire un tableau de std::pair.

8
andreee

En théorie, std::array devrait être initialisé avec une initialisation globale. Donc en fait ceci: 

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

est un sucre syntaxique pour ceci:

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

Comme vous le voyez, dans le premier cas, il semble que j'initialise un tableau avec des valeurs, mais il s'agit en réalité d'une initialisation d'agrégat avec une liste d'initialisation barrée. C'est clair comme un jour dans la deuxième situation. Donc, c'est pour commencer.

Ok, alors pourquoi ça ne marche pas?

std::array<std::pair<int, int>, 3> b {{1, 11}, {2, 22}, {3, 33}};

En bref, le compilateur ne peut pas distinguer le type de syntaxe que vous utilisez pour initialiser le tableau. {1, 11} peut être interprété à la fois comme une liste d'initialiseurs et utiliser la première version ou il peut être interprété comme une paire et aller avec la deuxième version. 

Ce code:

std::array<std::pair<int, int>, 3> b {{{1, 11}, {2, 22}, {3, 33}}};.

élimine l'ambiguïté.

Source: http://fr.cppreference.com/w/cpp/language/aggregate_initialization

0
bartop