Espacios de nombres
Variantes
Acciones

Orden de evaluación

De cppreference.com
< cpp‎ | language

El orden de evaluación de los operandos de casi todos los operadores de C++ no está especificado (incluyendo el orden de evaluación de los argumentos en una expresión de llamada a función y el orden de evaluación de subexpresiones en cualquier expresión). El compilador puede evaluar los operandos en cualquier orden, y podría seleccionar otro orden cuando se vuelve a evaluar la misma expresión.

Hay excepciones a esta regla que se detallan a continuación.

Excepto donde se indica a continuación, no existe un concepto de evaluación de izquierda a derecha o de derecha a izquierda en C ++. No se debe confundir con la asociatividad de izquierda a derecha y de derecha a izquierda de los operadores: la expresión f1() + f2() + f3() se analiza como (f1() + f2()) + f3() debido a la asociatividad de izquierda a derecha del operador +, pero la llamada a la función f3 se puede evaluar la primera, la última o entre f1() o f2() e tiempo de ejecución.


Contenido

[editar] Reglas secuenciado antes (desde C++11)

[editar] Definiciones

[editar] Evaluación de expresiones

La evaluación de cada expresión incluye:

  • cálculos de valor: cálculo del valor devuelto por la expresión. Esto puede conllevar la determinación de la identidad del objeto (evaluación glvalue, por ejemplo, si la expresión devuelve una referencia a algún objeto) o la lectura del un valor asigando a un objeto previamente (evaluación prvalue, por ejemplo, si la expresión devuelve un número, o cualquier otro valor)
  • Inicio de los efectos secundarios: acceso (lectura o escritura) a un objeto desigando por un glvalue volátil, modificación (escritura) de un objeto, llamada a una función de E/S de la biblioteca, o llamada a una función que hace cualquiera de esas operaciones.


[editar] Ordenando

Secuenciado antes es una relación asimétrica, transitiva y de pares entre evaluaciones dentro del mismo hilo.

  • Si A se secuencia antes que B, entonces la evaluación de A se debe completar antes de comenzar la evaluación de B.
  • Si no se secuencia antes que B y B se secuencia antes que A, entonces la evaluación de B se debe completar antes del comienzo de la evaluación de A.
  • Si A no se secuencia antes que B y B no se secuencia antes que A, existes dos posibilidades:
    • las evaluaciones de A y B no son secuenciales: se pueden realizar en cualquier orden y superponerse (con un sólo hilo de ejecución, el compilador puede intercalar las intrucciones de CPU de A y B)
    • las evaluaciones de A y B está secuenciadas sin determinar: se pueden realizar en cualquier orden pero no se pueden superponer: o se completa A antes que B, o B antes que A. el orden podría ser el contrarió la próxima vez que se evalúe la misma expresión.

[editar] Reglas

1) Cada valor de cálculo y efecto secundario de una expresión completa, es decir
incluyendo las conversiones implícitas aplicadas al resultado de la expresión, llamadas a destructor para temporales, inicializadores de miembro predeterminados (al inicializar agregados), y cualquier otro constructor del lenguaje que implique una llamada a función, es secuenciado antes de cada cálculo de valor y efectos secundarios de la siguiente expresión completa.
2) Los cálculos de valor (pero no los efectos secundarios) de los operandos de cualquier operador son secuenciados antes que el cálculo del valor del resultado del operador (pero no sus efectos secundarios).
3) Cuando se llama a una función (sea o no sea la función inline, y se use o no la sintaxis explícita de llamada a función), cada cálculo de valor y los efectos secundarios asociados con cualquier expresión de argumento, o con la expresión postfija que designa la función llamada, es secuenciada antes que la ejecución de cualquier expresión o sentencia en el cuerpo de dicha función.
4) El cálculo del valor del operador integrado post-incremento y post-decremento es secuenciado antes que sus efectos secundarios.
5) El efecto secundario del operador integrado pre-incremento y pre-decremento es secuenciado antes que el cálculo de su valor (regla implícita debido a la definición como asignación compuesta).
6) Cada cálculo de valor y efectos secundarios del primer argumento (izquierda) del operador integrado AND lógico && y del operador integrado OR lógico || es secuenciado antes que cualquier cálculo y efectos secundarios del segundo argumento (derecha).
7) Cada cálculo de valor y los efectos secundarios asociados con la primera expresión en el operador condicional ?: es secuenciado antes que cualquier cálculo de valor y efectos secundarios asociados con la segunda y tercera expresión.
8) El efecto secundario (modificación del argumento izquierdo) del operador integrado de asignación y todos los operadores integrados de asignación compuesta es secuenciado después que el cálculo del valor (pero no de los efectos secundarios) de los argumentos izquierdo y derecho, y es secuenciado antes que el cálculo del valor de la expresión de asignación (es decir, antes de devolver la referencia al objeto modificado)
9) Cada cálculo de valor y efecto secundario del primer argumento (izquierda) del operador coma integrado , es secuenciado antes que cualquier cálculo de valor y efecto secundario del segundo argumento (derecha).
10) En lista de inicialización, cada cálculo de valor y efecto secundario de una cláusula de inicialización dada es secuenciada antes que todo cálculo de valor y efecto secundario asociado con cualquier cláusula de inicialización que le siga en la lista de inicializadores separados por comas y entre corchetes.
11) Una llamada a función que no es secuenciada antes o secuenciada después que otra llamada a función es secuenciada sin determinar (el programa debe comportarse como si las instrucciones de la CPU que constituyen llamadas a diferentes funciones no se entrelazaron, incluso si las funciones eran inline).
La regla 11 tiene una excepción: las llamadas a funciones hechas por un algoritmo de la biblioteca estándar ejecutando bajo la política de ejecuciónstd::execution::par_unseq son sin secuenciar y pueden ser intercaladas arbitrariamente. (desde C++17)
12) La llamada a la función de asignación (operador new) es secuenciada sin determinar respecto a (hasta C++17) secuenciada antes que (desde C++17) la evaluación de los argumentos del constructor en una expresión new
13) Cuando se retorna de una función, la inicialización de copia del temporal que es el resultado de la evaluación de la llamada a la función es secuenciada antes de la destrucción de todos los temporales al final del operando de sentencia de retorno, que, a su vez, es secuenciada antes de la destrucción de las variables locales del bloque que encierra la sentencia de retorno.
(desde C++14)
14) En una expresión de llamada a función, la expresión que nombra la función es secuenciada antes que cualquier expresión de argumento y argumento predeterminado.
15) En una llamada a función, el cálculo de valores y los efectos secundarios de la incialización de cualquier parámetro son secuenciados sin determinar respecto al cálculo de valores y efectos secundarios de cualquier otro parámetro.
16) Cada operador sobrecargado sigue las reglas de secuenciación del operador integrado que sobrecarga cuando se llama mediante la notación del operador.
17) En una expresión de subíndice E1[E2], cada cálculo de valor y efecto secundario de E1 es secuenciado antes que cualquier cálculo de valor y efecto secundario de E2.
18) En una expresión de puntero a miembro E1.*E2 o E1->*E2, cada cálculo de valor y efecto secundario de E1 es secuenciado antes que cualquier cálculo de valor y efecto secundario de E2 (aunque el tipo dinámico de E1 no tenga el miembro al que se refiere E2)
19) En una expresión con el operador de desplazamiento E1<<E2 y E1>>E2, cada cálculo de valor y efecto secundario de E1 es secuenciado antes que cualquier cálculo de valor y efecto secundario de E2
20) En cualquier expresión de asignación simple E1=E2 y asignación compuesta E1@=E2, cada cálculo de valor y efecto secundario de E2 es secuenciado antes que cualquier cálculo de valor y efecto secundario de E1.
21) Cada expresión en una lista separada por comas de expresiones en una inicializador entre paréntesis se evaluá como una llamada a función (secuenciado sin determinar).
(desde C++17)

[editar] Comportamiento indeterminado

1) Si un efecto secundario en un objeto escalar no es secuenciado relativamente a otro efecto secundario sobre el mismo objeto, el comportamiento es indeterminado.

i = ++i + 2;       // comportamiento indeterminado hasta C++11
i = i++ + 2;       // comportamiento indeterminado hasta C++17
f(i = -2, i = -2); // comportamiento indeterminado hasta C++17
f(++i, ++i);       // comportamiento indeterminado hasta C++17, sin especificar desde C++17
i = ++i + i++;     //  comportamiento indeterminado


2) Si un efecto secundario sobre un objeto escalar no es secuenciado relativamente a un cálculo de valor usando el valor del mismo objeto, el comportamiento es indeterminado.

cout << i << i++; // comportamiento indeterminado hasta C++17
a[i] = i++;       // comportamiento indeterminado hasta C++17
n = ++i + i;      // comportamiento indeterminado


[editar] Reglas de punto de secuencia (hasta C++11)

[editar] Definiciones

La evaluación de una expresión puede provocar efectos secundarios, que son: acceso a un objeto designado por un lvalue volátil, modificar un objeto, llamada a un función de la biblioteca de E/S, o llamada a una función que realiza alguna de esas operaciones.

Un punto de secuencia es un punto en la secuencia de ejecución donde todos los efectos secundarios de la evaluación anterior en la secuencia están completos, y no se iniciaron efectos secundarios de las evaluaciones posteriores.

[editar] Reglas

1) Hay un punto de secuencia al final de cada expresión completa (normalmente, en el punto y coma).

2) Al llamar a una función (ya sea que la función sea inline o no, y si se usó o no la sintaxis de llamada a función), hay un punto de secuencia después de la evaluación de todos los argumentos de la función (si corresponde) que tiene lugar antes de la ejecución de cualquier expresión o declaración en el cuerpo de la función.

3) Hay un punto de secuencia después de la copia de un valor devuelto de una función y antes de la ejecución de cualquier expresión fuera de la función.

4) Una vez que comienza la ejecución de una función, no se evalúa ninguna expresión de la función de llamada hasta que se haya completado la ejecución de la función llamada (las funciones no se pueden intercalar).

5) En la evaluación de cada una de las siguientes cuatro expresiones, utilizando los operadores integrados (no sobrecargados), hay un punto de secuencia después de la evaluación de la expresión a.

a && b
a || b
a ? b : c
a , b

[editar] Comportamiento indeterminado

1) Entre el punto de secuencia anterior y el siguiente, un objeto escalar debe tener su valor almacenado modificado a lo sumo una vez por la evaluación de una expresión, de lo contrario el comportamiento es indeterminado.

i = ++i + i++; // comportamiento indeterminado
i = i++ + 1; // comportamiento indeterminado (hasta C++17)
i = ++i + 1; // comportamiento indeterminado (hasta C++11)
++ ++i; // comportamiento indeterminado (hasta C++11)
f(++i, ++i); // comportamiento indeterminado (hasta C++17)
f(i = -1, i = -1); // comportamiento indeterminado (hasta C++17)


2) Entre el punto de secuencia anterior y el siguiente, se debe acceder al valor anterior de un objeto escalar que se modifica por la evaluación de la expresión, solo para determinar el valor que se almacenará. Si se accede de alguna otra manera, el comportamiento es indeterminado.

cout << i << i++; // comportamiento indeterminado (hasta C++17)
a[i] = i++; // comportamiento indeterminado (hasta C++17)

[editar] Informes de defectos

Los siguientes informes de defectos de cambio de comportamiento se aplicaron de manera retroactiva a los estándares de C++ publicados anteriormente.

ID Aplicado a Comportamiento según lo publicado Comportamiento correcto
CWG 1885 C++14 la secuencia de destrucción de variables automáticas en el retorno de la función no era explícita se añade reglas de secuanciación

[editar] Referencias

  • C++11 standard (ISO/IEC 14882:2011):
  • 1.9 Ejecución de programa [intro.execution]
  • 5.2.6 Incremento y decremento [expr.post.incr]
  • 5.3.4 Operador 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]

[editar] Ver también

Documentacion C de Orden de evaluación