c++ - ¿Incrementar una variable utilizada dos veces en una lista de inicializadores-comportamiento indefinido?
undefined-behavior initializer (3)
Edit: Aún no se ha respondido: la pregunta vinculada era sobre valores r ordinarios, las listas de inicializadores son un concepto separado, si es relacionado.
¿Esta declaración está bien definida o está utilizando el operador de incremento de prefijo en una lista de inicializadores, en una variable que aparece dos veces en la lista, comportamiento indefinido?
struct T t = { i, ++i };
Estoy más interesado en ANSI C, pero también sería útil saber si otras versiones de C y / o C ++ difieren. Y si construcciones similares como las siguientes son legales:
struct T t = { i, i++ };
struct T t = { ++i, ++i };
struct T t = { i++, ++i };
struct T t = { i++, i++ };
do
En C (no necesariamente la misma respuesta que para C ++), no hay puntos de secuencia asociados con los componentes de una lista de inicializadores.
El estándar C11, ISO / IEC 9899: 2011, dice en la sección §6.7.9 Inicialización :
¶19 La inicialización se producirá en el orden de la lista de inicializadores, cada inicializador proporcionado para un subobjeto en particular anulará cualquier inicializador previamente listado para el mismo subobjeto; 151)
151) Cualquier inicializador para el subobjeto que se invalida y, por lo tanto, no se utiliza para inicializar ese subobjeto puede no ser evaluado en absoluto.
Eso suena prometedor, pero ...
¶23 Las evaluaciones de las expresiones de la lista de inicialización se secuencian de manera indeterminada una con respecto a la otra y, por lo tanto, no se especifica el orden en que ocurren los efectos secundarios. 152)
152) En particular, el orden de evaluación no tiene que ser el mismo que el orden de inicialización del subobjeto.
Por lo tanto, (en C) el orden de evaluación se secuencia en forma indeterminada, y no puede confiar en cuándo se producen los incrementos (o, en casos extremos no ilustrados por el código en la pregunta, si se producen los incrementos).
En C99 (ISO / IEC 9899: 1999), el número de sección es §6.7.8, pero los párrafos 19 y 23 tienen esencialmente el mismo contenido, excepto que los números de las notas al pie son diferentes.
En C90 (ISO / IEC 9899: 1990), el problema no se aborda explícitamente.
C ++
A juzgar por la songyuanyao de songyuanyao , las reglas en C ++ 11 (y posteriores) son diferentes de las de C11. Este tipo de cosas enfatiza que los idiomas C y C ++ son diferentes y hace que escribir respuestas completas a las preguntas etiquetadas con ambos idiomas sea extremadamente difícil.
Preguntas estrechamente relacionadas
Hay al menos otras dos preguntas relacionadas con los efectos secundarios (como ++
) en contextos distintos a los inicializadores. Ambos deben ser leídos también. El segundo, en particular, es de interés para los usuarios de C ++; el primero está etiquetado como C y no C ++, por lo que es de mayor relevancia para aquellos interesados en C.
Ambos fueron señalados por πάντα ῥεῖ en los comentarios.
En C11 el comportamiento de todas estas inicializaciones no está indefinido. Ver 6.7.9 / 23:
Las evaluaciones de las expresiones de la lista de inicialización se secuencian de forma indeterminada una con respecto a la otra y, por lo tanto, no se especifica el orden en que aparecen los efectos secundarios.
El término secuenciado de forma indeterminada se define como tal (5.1.2.3):
Las evaluaciones A y B se secuencian de forma indeterminada cuando A se secuencia antes o después de B, pero no se especifica cuál.
En C99, el lenguaje utilizado no estaba claramente redactado en cuanto a si se trata de la misma situación o comportamiento indefinido. En C89, el problema no se menciona en absoluto, por lo que probablemente deberíamos suponer que en C89 no están definidos.
C ++ 11 y posteriores
El comportamiento está bien definido para la inicialización de la lista . De acuerdo con las reglas de secuencia antes (desde C ++ 11):
10) En la inicialización de lista, cada cálculo de valor y efecto secundario de una cláusula de inicialización dada se secuencia antes de cada cálculo de valor y efecto secundario asociado con cualquier cláusula de inicialización que sigue en la lista de inicializadores separados por comas.
Así que para struct T t = { i, ++i };
, se evaluará al principio, y luego ++i
, el orden está bien definido. Y todas las otras muestras estarían bien también.
Cotizaciones del estándar de C ++, $ 8.6.4 / 4 List-initialization [dcl.init.list] :
(énfasis mío)
Dentro de la lista de inicializadores de una lista de iniciados, las cláusulas de inicialización, incluidas las que resulten de expansiones de paquetes ([temp.variadic]), se evalúan en el orden en que aparecen . Es decir, cada cálculo de valor y efecto secundario asociado con una cláusula de inicialización dada se secuencia antes de cada cálculo de valor y efecto secundario asociado con cualquier cláusula de inicializador que sigue en la lista separada por comas de la lista de inicializador. [Nota: este orden de evaluación se mantiene independientemente de la semántica de la inicialización; por ejemplo, se aplica cuando los elementos de la lista de inicializadores se interpretan como argumentos de una llamada de constructor, aunque normalmente no hay restricciones de secuencia en los argumentos de una llamada. - nota final]