¿Por qué la inicialización de la matriz de pares todavía necesita llaves dobles en C++ 14?
c++14 initializer-list (5)
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 la matriz con valores, pero en realidad es una inicialización agregada con una lista de iniciación. Esto queda claro como un día en la segunda situación. Así que eso es para empezar.
Ok, ¿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}
puede interpretarse como lista de inicialización y usar la primera versión o puede interpretarse 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
Con el estándar C ++ 14, la inicialización de un
std::array
puede ir con llaves simples (consulte
http://en.cppreference.com/w/cpp/container/array
):
Esto, sin embargo, no funciona para un
std::array
of
std::pair
.
¿Por qué funcionan estos:
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 que esto funciona de nuevo?
std::array<std::pair<int, int>, 3> b {{{1, 11}, {2, 22}, {3, 33}}};
Además, para completar, la inicialización de una buena matriz antigua funciona con llaves individuales
std::pair<int, int> c[3] {{1, 11}, {2, 22}, {3, 33}};
Esto parece ser una ambuguidad de análisis algo similar al famoso y desconcertante análisis . Sospecho que lo que está pasando es que:
Si tú escribes
std::array<std::pair<int, int>, 3> b {{1, 11}, {2, 22}, {3, 33}};
El compilador tiene dos formas de interpretar la sintaxis:
-
Realiza una inicialización de refuerzo completo (lo que significa que el soporte externo se refiere a la inicialización agregada de la
std::array
, mientras que la primera más interna inicializa la representación interna de miembro de lastd::array
que es una matriz C real). Esto no podrá compilarse, ya que posteriormentestd::pair<int, int>
no se puede inicializar con1
(todas las llaves están agotadas). clang dará un error de compilación que indica exactamente 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}}; ^
Tenga en cuenta también que este problema se resuelve si no hay un agregado interno de miembros que deba inicializarse, es decir,
std::pair<int, int> b[3] = {{1, 11}, {2, 22}, {3, 33}};
Se compilará bien como inicialización agregada.
-
(De la forma en que lo dijiste). Realizas una inicialización con llaves, las llaves más internas, por lo tanto, son para la inicialización agregada de los pares individuales, mientras que las llaves para las representaciones de la matriz interna se eliminan. Tenga en cuenta que incluso si no existiera esta ambigüedad, como se señaló correctamente en la respuesta de rustyx , las reglas de la orientación 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 las llaves adicionales, realiza la inicialización completa de la llave y levanta cualquier ambigüedad sintáctica.
La regla de elision de refuerzo de C ++ 14 se aplica solo a la inicialización de subagregados.
Así, por ejemplo, algo como esto funciona:
std::array<std::array<int, 3>, 3> a{1, 11, 2, 22, 3, 33};
Aquí se puede inicializar una lista de agregados sin llaves adicionales.
Pero
std::pair
no es un
aggregate
(tiene constructores), por lo que la regla no se aplica.
Lo que significa que sin la regla de
std::array
de llaves,
std::array
, que es un conjunto con una matriz en su interior, necesita un conjunto extra de llaves para poder iniciar la
lista
.
Recuerde que la
array
plantillas de clase se implementa como:
template<typename T, std::size_t N>
struct array {
T elems[N];
};
Para
listarlo-inicializarlo
sin la regla de
elems
de llaves, necesita un conjunto adicional de llaves para llegar al miembro
elems
.
Sin las llaves dobles, la afirmació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á a
{ {1,2} }
como una
lista de inicialización escalar
para la
array<int, 2>
.
Debe declarar una lista
-iniciada
anidada
explícita para que el compilador reconozca que la lista interna también está
inicializada de forma agregada
(frente a la escalable inicializada), de modo que pueda construir una matriz de
std::pair
.
Voy a adivinar aquí.
La lista de inicializadores para
std::array<T,n>
debe ser una lista de
T
(o trivialmente construible para
T
).
Para que pudieras 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 verboso.
Para obtener la conversión a
std::pair<int,int>
que desea, debe proporcionar una lista de inicialización, por lo que
std::array<std::pair<int,int>,3> b {
{ // element 1
{ // initialize from:
{ 1,11 } // std::initializer_list
}
},
...
};
No puedo seguir defendiendo esto, pero tenga en cuenta 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 es.
Tampoco está definido
std::pair<U,T>::pair( std::initializer_list<??> )
.