¿Por qué la inicialización de la matriz de pares todavía necesita llaves dobles en C++14?


Con el estándar C++14, la inicialización de un std::array puede ir con llaves simples (ver http://en.cppreference.com/w/cpp/container/array):

Esto, sin embargo, no funciona para un std::array de std::pair.

Por qué funcionan:

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

Pero esto no funciona:

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

Mientras esto funciona de nuevo?

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

También, para completar, la inicialización de un buen array viejo funciona con llaves simples

std::pair<int, int> c[3] {{1, 11}, {2, 22}, {3, 33}};
Author: John Kugelman, 2018-05-30

5 answers

Esto parece ser una ambuguidad de análisis algo similar al famoso análisis más irritante. Sospecho que lo que está pasando es que:

Si escribes

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

El compilador tiene dos maneras de interpretar la sintaxis:

  1. Realiza una inicialización de corchete completo (lo que significa que la corchete más externa se refiere a la inicialización agregada de std::array, mientras que la primera más interna inicializa la representación interna del miembro de std::array que es una matriz C real). Esto fallará al compilar, ya que std::pair<int, int> posteriormente no puede ser inicializado por 1 (todas las llaves se usan). clang dará un error del compilador indicando exactamente eso:

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

    Tenga en cuenta también que este problema se resuelve si no hay un agregado de miembro interno que se inicialice, es decir,

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

    Compilará bien como inicialización agregada.

  2. (Como lo querías decir.) Se realiza una inicialización de brace-elided, los brackets más internos por lo tanto son para aggregate-inicialización de los pares individuales, mientras que las llaves para las representaciones internas del array son elididas. Tenga en cuenta que incluso si no hubiera esta ambigüedad, como se señaló correctamente en la respuesta de rustyx, las reglas de elisión de llaves no se aplican ya que std::pair no es un tipo agregado, por lo que el programa aún estaría mal formado.

El compilador preferirá la opción 1. Al proporcionar los apoyos adicionales, usted realiza la inicialización completa y levanta cualquier sintáctica ambigüedad.

 28
Author: Jodocus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-30 13:24:26

La regla de elisión de llaves de C++14 se aplica solo a la inicialización de subagregaciones.

Así que, por ejemplo, algo como esto funciona:

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

Aquí se puede inicializar un agregado de agregados sin llaves adicionales.

Pero std::pair no es un agregado (tiene constructores), por lo que la regla no se aplica.

Lo que significa que sin la regla de elisión de llaves, std::array, siendo en sí mismo un agregado con una matriz dentro, necesita un conjunto adicional de llaves para be list-initialized. Recuerde que la plantilla de clase array se implementa como:

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

Para list-inicializarlo sin la regla de elisión de llaves, necesita un conjunto adicional de llaves para llegar al miembro elems.

 14
Author: rustyx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-30 13:48:18

Sin las llaves dobles, la declaración es simplemente ambigua. Considere el siguiente código:

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

Sin llaves dobles en la primera definición, el compilador tratará { {1,2} } como una lista de inicialización escalar para array<int, 2>. Necesita declarar una anidada-init-list para que el compilador reconozca que la lista interna también es aggregate-initialized (vs.scalar initialized), de modo que pueda construir un array de std::pair.

 8
Author: andreee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-17 06:32:30

En teoría std::array debe inicializarse con inicialización agregada. Así que en realidad esto:

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

Es un azúcar sintáctico para esto:

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

Como puede ver, en el primero parece que inicializo array con valores, pero en realidad es inicialización agregada con lista de inicio entre corchetes. Esto es claro como un día en la segunda situación. Así que eso es para empezar.

Ok Entonces, ¿por qué no funciona esto?

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

Bueno, en pocas palabras - el compilador no puede distinguir qué tipo de sintaxis Está utilizando para inicializar la matriz. {1, 11} se puede interpretar tanto como lista inicializadora y usar la primera versión o se puede interpretar como un par e ir con la segunda versión.

Este código:

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

Elimina la ambigüedad.

Fuente: http://en.cppreference.com/w/cpp/language/aggregate_initialization

 0
Author: bartop,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-30 07:33:51

Voy a adivinar aquí.
La lista inicializadora para std::array<T,n> debe ser una lista de T (o trivialmente construible para T). Así que usted podría hacer

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

Pero eso es tediosamente prolijo. Para obtener la conversión a std::pair<int,int> que desea, debe proporcionar una lista de inicializadores, por lo que

std::array<std::pair<int,int>,3> b {
    { // element 1
      { // initialize from:
        { 1,11 } // std::initializer_list
       }
     },
  ...
};

No puedo defender esto más, pero note que std::vector<T, Allocator>::vector( std::initializer_list<T>, const Allocator& alloc=Allocator()) está definido pero std::array<T,n>::array( std::initializer_list<T> ) no lo está. Tampoco se define std::pair<U,T>::pair( std::initializer_list<??> ).

 -2
Author: jwm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-30 07:21:31