c++ - studio - ¿Cuándo se pueden omitir las llaves externas en una lista de inicializadores?
visual studio installer (1)
Tengo el error C2078 en VC2010 al compilar el código a continuación.
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};
Descubrí que la sintaxis correcta para a1
es
std::array<A, 2> a1 =
{{
{0, 0.1},
{2, 3.4}
}};
La pregunta es: ¿por qué se requieren llaves adicionales para a1
pero no son necesarias para a2
?
Actualizar
La pregunta parece no ser específica de std :: array. Algunos ejemplos:
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}}};
Todavía no veo por qué struct D
da el error, pero B y C no.
Los refuerzos adicionales son necesarios porque std::array
es un agregado y POD, a diferencia de otros contenedores en la biblioteca estándar. std::array
no tiene un constructor definido por el usuario. Su primer miembro de datos es una matriz de tamaño N
(que pasa como un argumento de plantilla), y este miembro se inicializa directamente con un inicializador. Las llaves adicionales son necesarias para la matriz interna que se inicializa directamente.
La situación es la misma que:
//define this aggregate - no user-defined constructor
struct Aarray
{
A data[2]; //data is an internal array
};
¿Cómo inicializarías esto? Si haces esto:
Aarray a1 =
{
{0, 0.1},
{2, 3.4}
};
da un error de compilación :
error: demasiados inicializadores para ''Aarray''
Este es el mismo error que obtienes en el caso de std::array
(si usas GCC).
Entonces, lo correcto es usar llaves de la siguiente manera:
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
};
que compila bien . Una vez más, se necesitan llaves adicionales porque estás inicializando la matriz interna .
-
Ahora la pregunta es por qué no se necesitan frenos adicionales en caso de que se double
.
Es porque el double
no es un agregado, mientras que A
es.En otras palabras, std::array<double, 2>
es un agregado de agregado, mientras std::array<A, 2>
es un agregado de agregado de agregado 1 .
1. Creo que todavía se necesitan frenos adicionales en el caso del doble también (como this ), para estar completamente de acuerdo con el estándar, pero el código funciona sin ellos. ¡Parece que necesito profundizar en las especificaciones otra vez! .
Más sobre tirantes y llaves extra
Excavé a través de las especificaciones. Esta sección (§8.5.1 / 11 de C ++ 11) es interesante y se aplica a este caso:
En una declaración de la forma
T x = { a };
Las llaves se pueden elid en una lista de inicializadores de la siguiente manera . Si la lista de inicialización comienza con un paréntesis izquierdo, la siguiente lista separada por comas de cláusulas de inicializador inicializa los miembros de un subaggregado; es erróneo que haya más cláusulas de inicialización que miembros. Sin embargo, si la lista de inicialización de un subaggregado no comienza con un paréntesis izquierdo, solo se tomarán suficientes cláusulas de inicializador de la lista para inicializar los miembros del subaggregado; cualquier cláusula de inicializador restante se deja para inicializar el siguiente miembro del agregado del cual el subaggregado actual es miembro. [Ejemplo:
float y[4][3] = {
{ 1, 3, 5 },
{ 2, 4, 6 },
{ 3, 5, 7 },
};
es una inicialización completamente arriostrada: 1, 3 y 5 inicializan la primera fila de la matriz
y[0]
, es decir,y[0][0]
,y[0][1]
, yy[0][2]
. Del mismo modo, las siguientes dos líneas inicializany[1]
yy[2]
. El inicializador finaliza temprano y, por lo tanto, los elementos dey[3]s
se inicializan como si se inicializaran explícitamente con una expresión de la forma float (), es decir, se inicializan con 0.0. En el siguiente ejemplo, las llaves en la lista de inicializadores son elidas; sin embargo, la lista de inicializadores tiene el mismo efecto que la lista de inicializadores completamente arriostrados del ejemplo anterior,
float y[4][3] = {
1, 3, 5, 2, 4, 6, 3, 5, 7
};
El inicializador para y comienza con un paréntesis izquierdo, pero el de
y[0]
no, por lo tanto, se utilizan tres elementos de la lista. Del mismo modo, los siguientes tres se toman sucesivamente paray[1]
yy[2]
. -Final ejemplo]
En base a lo que entendí de la cita anterior, puedo decir que debería permitirse lo siguiente:
//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}
}};
En el primero, los apoyos para el agregado interno están completamente eliminados, mientras que el segundo tiene una inicialización totalmente reforzada. En su caso (el caso del double
), la inicialización usa el primer enfoque (las llaves se eliran por completo para el agregado interno).
Pero esto debería ser rechazado:
//ILL-FORMED : neither braces-elided, nor fully-braced
std::array<A, 2> Z =
{
{0, 0.1},
{2, 3.4}
};
No es ni apoyado por llaves, ni hay llaves suficientes para una inicialización completamente reforzada. Por lo tanto, está mal formado.