Order of evaluation
El orden de evaluación de cualquier parte de cualquier expresión, incluyendo el orden de evaluación de los argumentos de función es no especificado (con algunas excepciones listadas abajo). El compilador puede evaluar operandos y otras subexpresiones en cualquier orden, y puede elegir otro orden cuando la misma expresión es evaluada nuevamente.
No existe el concepto de evaluación de izquierda a derecha o de derecha a izquierda en C++. Esto no debe confundirse con la asociatividad de izquierda a derecha y de derecha a izquierda de los operadores: la expresión a ( ) + b ( ) + c ( ) se analiza como ( a ( ) + b ( ) ) + c ( ) debido a la asociatividad de izquierda a derecha del operator + , pero c ( ) podría evaluarse primero, al último, o entre a ( ) o b ( ) en tiempo de ejecución:
Salida posible:
b c a c a b
Contenidos |
Reglas de "secuenciado antes" (desde C++11)
Evaluación de Expresiones
La evaluación de cada expresión incluye:
- Cálculos de valor : cálculo del valor que devuelve la expresión. Esto puede implicar la determinación de la identidad del objeto (evaluación glvalue, p.ej. si la expresión devuelve una referencia a algún objeto) o la lectura del valor previamente asignado a un objeto (evaluación prvalue, p.ej. si la expresión devuelve un número u otro valor).
- Iniciación de efectos secundarios : acceso (lectura o escritura) a un objeto designado por un glvalue volátil, modificación (escritura) de un objeto, llamada a una función de E/S de biblioteca, o llamada a una función que realice cualquiera de estas operaciones.
Ordenación
Secuenciado antes
es una relación asimétrica, transitiva y de pares entre evaluaciones
A
y
B
dentro del mismo hilo.
-
Si
Aestá secuenciado antes deB(o, equivalentemente,Bestá secuenciado después deA), entonces la evaluación deAse completará antes de que comience la evaluación deB. -
Si
Ano está secuenciado antes deByBestá secuenciado antes deA, entonces la evaluación deBse completará antes de que comience la evaluación deA. -
Si
Ano está secuenciado antes deByBno está secuenciado antes deA, entonces existen dos posibilidades:-
Las evaluaciones de
AyBestán sin secuenciar : pueden realizarse en cualquier orden y pueden superponerse (dentro de un único hilo de ejecución, el compilador puede entrelazar las instrucciones de CPU que componenAyB). -
Las evaluaciones de
Ay B están indeterminadamente secuenciadas : pueden realizarse en cualquier orden pero no pueden superponerse: ya sea queAse complete antes deB, oBse complete antes deA. El orden puede ser opuesto la próxima vez que se evalúe la misma expresión.
-
Las evaluaciones de
Se dice que una expresión X está secuenciada antes que una expresión Y si cada cálculo de valor y cada efecto secundario asociado con X está secuenciado antes de cada cálculo de valor y cada efecto secundario asociado con la expresión Y .
Reglas
- cada expresión de argumento y la expresión postfija que designa func
|
(desde C++26) |
- cada expresión o sentencia en el cuerpo de func
|
(desde C++26) |
| La regla 10 tiene una excepción: las llamadas a función realizadas por un algoritmo de la biblioteca estándar ejecutándose bajo la política de ejecución std::execution::par_unseq no están secuenciadas y pueden estar arbitrariamente intercaladas entre sí. | (since C++17) |
|
13)
En una expresión de llamada a función, la expresión que nombra la función se secuencia antes de cada expresión de argumento y cada argumento por defecto.
14)
En una llamada a función, los cálculos de valor y efectos secundarios de la inicialización de cada parámetro tienen secuenciación indeterminada respecto a los cálculos de valor y efectos secundarios de cualquier otro parámetro.
15)
Cada operador sobrecargado obedece a las reglas de secuenciación del operador incorporado que sobrecarga cuando se llama usando notación de operador.
16)
En una expresión de subíndice
E1
[
E2
]
,
E1
se secuencia antes que
E2
.
17)
En una expresión de puntero a miembro
E1.
*
E2
o
E1
-
>
*
E2
,
E1
se secuencia antes que
E2
(a menos que el tipo dinámico de
E1
no contenga el miembro al que
E2
hace referencia).
18)
En una expresión de operador de desplazamiento
E1
<<
E2
y
E1
>>
E2
,
E1
se secuencia antes que
E2
.
19)
En cada expresión de asignación simple
E1
=
E2
y cada expresión de asignación compuesta
E1 @
=
E2
,
E2
se secuencia antes que
E1
.
20)
Cada expresión en una lista separada por comas de expresiones en un inicializador entre paréntesis se evalúa como si fuera para una llamada a función (secuenciación indeterminada).
|
(desde C++17) |
Comportamiento indefinido
El comportamiento es undefined en los siguientes casos:
i = ++i + 2; // bien definido i = i++ + 2; // comportamiento indefinido hasta C++17 f(i = -2, i = -2); // comportamiento indefinido hasta C++17 f(++i, ++i); // comportamiento indefinido hasta C++17, no especificado después de C++17 i = ++i + i++; // comportamiento indefinido
cout << i << i++; // undefined behavior until C++17 a[i] = i++; // undefined behavior until C++17 n = ++i + i; // undefined behavior
- un side effect en la misma ubicación de memoria
- un value computation utilizando el valor de cualquier objeto en la misma ubicación de memoria
- iniciar o finalizar el lifetime de un objeto que ocupa almacenamiento que se superpone con la ubicación de memoria
union U { int x, y; } u; (u.x = 1, 0) + (u.y = 2, 0); // undefined behavior
Reglas de puntos de secuencia (hasta C++11)
Definiciones Previas a C++11
La evaluación de una expresión podría producir efectos secundarios, los cuales son: acceder a un objeto designado por un lvalue volatile, modificar un objeto, llamar a una función de E/S de la biblioteca, o llamar a una función que realice cualquiera de esas operaciones.
Un punto de secuencia es un punto en la secuencia de ejecución donde todos los efectos secundarios de las evaluaciones previas en la secuencia están completos, y ningún efecto secundario de las evaluaciones posteriores ha comenzado.
Reglas Pre-C++11
a && b a || b a ? b : c a , b
Comportamiento indefinido anterior a C++11
El comportamiento es indefinido en los siguientes casos:
i = ++i + i++; // undefined behavior i = i++ + 1; // undefined behavior i = ++i + 1; // undefined behavior ++ ++i; // undefined behavior f(++i, ++i); // undefined behavior f(i = -1, i = -1); // undefined behavior
cout << i << i++; // undefined behavior a[i] = i++; // undefined behavior
Informes de defectos
Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares de C++ publicados anteriormente.
| DR | Aplicado a | Comportamiento publicado | Comportamiento correcto |
|---|---|---|---|
| CWG 1885 | C++11 |
la secuenciación de la destrucción de variables
automáticas al retornar de función no era explícita |
se añadieron reglas de secuenciación |
| CWG 1949 | C++11 |
se utilizaba "secuenciado después" pero no estaba
definido en el estándar de C++ |
definido como el inverso
de "secuenciado antes" |
| CWG 1953 | C++11 |
los efectos secundarios y cálculos de valor que involucran
una ubicación de memoria podrían no estar secuenciados respecto al inicio o fin del tiempo de vida de un objeto en la misma ubicación de memoria |
el comportamiento es
indefinido en este caso |
| CWG 2146 | C++98 |
los casos que involucran comportamientos indefinidos
no consideraban campos de bits |
considerados |
Referencias
- Estándar C++23 (ISO/IEC 14882:2024):
-
- 6.9.1 Ejecución del programa [intro.execution]
-
- 7.6.1.6 Incremento y decremento [expr.post.incr]
-
- 7.6.2.8 New [expr.new]
-
- 7.6.14 Operador AND lógico [expr.log.and]
-
- 7.6.15 Operador OR lógico [expr.log.or]
-
- 7.6.16 Operador condicional [expr.cond]
-
- 7.6.19 Operadores de asignación y asignación compuesta [expr.ass]
-
- 7.6.20 Operador coma [expr.comma]
-
- 9.4.5 Inicialización de lista [dcl.init.list]
- Estándar C++20 (ISO/IEC 14882:2020):
-
- 6.9.1 Ejecución del programa [intro.execution]
-
- 7.6.1.5 Incremento y decremento [expr.post.incr]
-
- 7.6.2.7 New [expr.new]
-
- 7.6.14 Operador AND lógico [expr.log.and]
-
- 7.6.15 Operador OR lógico [expr.log.or]
-
- 7.6.16 Operador condicional [expr.cond]
-
- 7.6.19 Operadores de asignación y asignación compuesta [expr.ass]
-
- 7.6.20 Operador coma [expr.comma]
-
- 9.4.4 Inicialización de lista [dcl.init.list]
- Estándar C++17 (ISO/IEC 14882:2017):
-
- 4.6 Ejecución del programa [intro.execution]
-
- 8.2.6 Incremento y decremento [expr.post.incr]
-
- 8.3.4 New [expr.new]
-
- 8.14 Operador AND lógico [expr.log.and]
-
- 8.15 Operador OR lógico [expr.log.or]
-
- 8.16 Operador condicional [expr.cond]
-
- 8.18 Operadores de asignación y asignación compuesta [expr.ass]
-
- 8.19 Operador coma [expr.comma]
-
- 11.6.4 Inicialización de lista [dcl.init.list]
- Estándar C++14 (ISO/IEC 14882:2014):
-
- 1.9 Ejecución del programa [intro.execution]
-
- 5.2.6 Incremento y decremento [expr.post.incr]
-
- 5.3.4 New [expr.new]
-
- 5.14 Operador AND lógico [expr.log.and]
-
- 5.15 Operador OR lógico [expr.log.or]
-
- 5.16 Operador condicional [expr.cond]
-
- 5.17 Operadores de asignación y asignación compuesta [expr.ass]
-
- 5.18 Operador coma [expr.comma]
-
- 8.5.4 Inicialización de lista [dcl.init.list]
- Estándar C++11 (ISO/IEC 14882:2011):
-
- 1.9 Ejecución del programa [intro.execution]
-
- 5.2.6 Incremento y decremento [expr.post.incr]
-
- 5.3.4 New [expr.new]
-
- 5.14 Operador lógico AND [expr.log.and]
-
- 5.15 Operador lógico OR [expr.log.or]
-
- 5.16 Operador condicional [expr.cond]
-
- 5.17 Operadores de asignación y asignación compuesta [expr.ass]
-
- 5.18 Operador coma [expr.comma]
-
- 8.5.4 Inicialización de lista [dcl.init.list]
Véase también
- Precedencia de operadores que define cómo se construyen las expresiones a partir de su representación en código fuente.
|
Documentación de C
para
Orden de evaluación
|