¿Puedo hacer referencia a Miembros Anteriores de una Lista Inicializadora?


Digamos que quiero hacer referencia a un miembro de un initializer_list que ya he definido. ¿Puedo hacerlo?

Este código compila y da el esperado: "13 55" tanto en Visual Studio como en gcc , me gustaría saber que es legal:

const int foo[2] = {13, foo[0] + 42};
Author: Shafik Yaghmour, 2015-11-20

1 answers

Así que lo que tenemos aquí es la inicialización agregada cubierta en la sección 8.5.1 del borrador del estándar C++ y dice:

Un agregado es una matriz o una clase [...]

Y:

Cuando un agregado es inicializado por una lista inicializadora, como se especifica en 8.5.4, los elementos de la lista inicializador se toman como inicializadores para los miembros del agregado, en subíndice creciente o la orden del miembro. Cada miembro es copiado-inicializado desde el correspondiente inicializador-cláusula [...]

Aunque parece razonable que los efectos secundarios de la inicialización de cada miembro del agregado deben ser secuenciados antes del siguiente, ya que cada elemento en la lista del inicializador es una expresión completa. El estándar en realidad no garantiza esto podemos ver esto en informe de defectos 1343 que dice:

La redacción actual no indica que la inicialización de un objeto no-clase es una expresión completa, pero presumiblemente debería hacerlo.

Y también señala:

La inicialización agregada también podría involucrar más de una expresión completa, por lo que la limitación anterior a "inicialización de un objeto no-clase" no es correcta.

Y podemos ver en un tema relacionado std-discusión Richard Smith dice:

[intro.ejecución] p10 :" Una expresión completa es una expresión que no es una subexpresión de otra expresión. [...] Si una construcción de lenguaje se define para producir una llamada implícita de una función, un uso de la la construcción del lenguaje se considera una expresión para los propósitos de esta definición."

Dado que una lista de inicio entre corchetes no es una expresión, y en este caso no resulta en una llamada a la función, 5 y s. i están separados expresiones completas. Entonces:

[intro.ejecución] p14: "Cada cálculo de valor y efecto secundario asociado a una expresión completa es secuenciado antes de cada valor cálculo y efectos secundarios asociado con la siguiente expresión completa por evaluar."

Así que la única pregunta es, es el efecto secundario de inicializar s. i "asociado con" la evaluación de la expresión "5"? Creo que la única suposición razonable es que es: si 5 inicializara un miembro del tipo de clase, la llamada al constructor obviamente sería parte de la expresión completa por la definición en [intro.ejecución] p10, por lo que es natural asumir que lo mismo es cierto para los tipos escalares.

Sin embargo, no creo que el estándar en realidad diga explícitamente esto dondequiera.

Así que esto actualmente no está especificado por el estándar y no se puede confiar en él, aunque me sorprendería si una implementación no lo tratara de la manera que espera.

Para un caso simple como este algo similar a este parece una mejor alternativa:

constexpr int value = 13 ;
const int foo[2] = {value, value+42};

Cambios En C++17

La propuesta P0507R0: Cuestión básica 1343: Secuenciación de no clases la inicialización clarifica el punto de expresión completa que aparece aquí pero no responde a la pregunta sobre si el efecto secundario de la inicialización se incluye en la evaluación de la expresión completa. Por lo tanto, no cambia que esto no esté especificado.

Los cambios relevantes para esta pregunta están en [intro.ejecución]:

Una expresión constituyente se define de la siguiente manera:

(9.1) - La expresión constituyente de una expresión es que expresion.

(9.2) - Las expresiones constitutivas de una lista de inicio entre corchetes o de una lista de expresiones (posiblemente entre paréntesis) son expresiones constitutivas de los elementos de la lista respectiva.

(9.3) - Las expresiones constituyentes de un inicializador brace-or-equal de la cláusula form = initializer-son las expresiones constitutivas de la cláusula initializer. [ Ejemplo:

struct A { int x; };
struct B { int y; struct A a; };
B b = { 5, { 1+1 } };

Las expresiones constitutivas del inicializador utilizadas para la inicialización de b son 5 y 1 + 1. - ejemplo final]

And [intro.ejecución] p12 :

Una expresión completa es

(12.1) - un operando no evaluado (Cláusula 8),

(12.2) - una expresión constante (8.20),

(12.3) - un declarador de inicio (Cláusula 11) o un inicializador mem (15.6.2), incluidas las expresiones constitutivas de la inicializador,

(12.4) - una invocación de un destructor generado al final de la vida útil de un objeto que no sea temporal objeto (15.2), o

(12.5) - una expresión que no es una subexpresión de otra expresión y que no forma parte de una expresión completa.

Así que en este caso tanto 13 como foo[0] + 42son expresión constituyenteque son parte de una expresión completa. Esto es una ruptura con el análisis aquí que postulaba que cada uno sería su propia expresión completa.

Cambios En C++20

La Propuesta de inicialización designada: P0329 contiene la siguiente adición que parece hacer esto bien definido:

Añádase un nuevo párrafo a 11.6.1 [dcl.init.aggr]:

Las inicializaciones de los elementos del agregado se evalúan en el orden de los elementos. Eso es, todos los cálculos de valor y efectos secundarios asociados con un elemento dado se secuencian antes que los de cualquier elemento que lo siga en orden.

Podemos ver que esto se refleja en el último borrador de la norma.

 19
Author: Shafik Yaghmour,
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-13 19:50:48