Entonces, ¿por qué i = ++ i + 1 está bien definido en C ++ 11?

He visto las otras preguntas similares y leí el defecto al respecto. Pero todavía no lo entiendo ¿Por qué i = ++i + 1 bien definido en C ++ 11 cuando i = i++ + 1 no? ¿Cómo el estándar lo hace bien definido?

Al hacer ejercicio, tengo la siguiente secuencia antes del gráfico (donde una flecha representa la relación secuenciada anterior y todo es un cálculo de valor a menos que se especifique lo contrario):

 i = ++i + 1 ^ | assignment (side effect on i) ^ ^ | | ☆i ++i + 1 || ^ i+=1 | ^ 1 | ★assignment (side effect on i) ^ ^ | | i 1 

Marqué un efecto secundario en i con una estrella negra y el cálculo de valor de i con una estrella blanca. Estos parecen no ser secuenciados el uno con el otro (según mi lógica). Y el estándar dice:

Si un efecto secundario en un objeto escalar no se está secuenciando en relación con otro efecto secundario en el mismo objeto escalar o con un cálculo de valor que utiliza el valor del mismo objeto escalar, el comportamiento no está definido.

La explicación en el informe de defectos no me ayudó a entender. ¿Qué tiene que ver la conversión lvalue-to-rvalue con algo? ¿Qué me he equivocado?

… o un cálculo de valor usando el valor del mismo objeto escalar …

La parte importante está en negrita aquí. El lado izquierdo no usa el valor de i para el cálculo del valor. Lo que se está calculando es un glvalue . Solo después (secuencia después), el valor del objeto se toca y se reemplaza.

Desafortunadamente este es un punto muy sutil 🙂

Bueno, el momento clave aquí es que el resultado de ++i es un lvalue . Y para participar en binario + lvalue debe convertirse a un valor r por conversión lvalue a rvalue. La conversión de Lvalue a Rvalue es básicamente un acto de lectura de la variable i de la memoria.

Eso significa que se supone que el valor de ++i se obtiene como si se leyera directamente desde i . Eso significa que conceptualmente el nuevo valor (incrementado) de i debe estar listo (debe almacenarse físicamente en i ) antes de que comience la evaluación de binario + .

Esta secuencia adicional es lo que inadvertidamente hizo el comportamiento definido en este caso.

En C ++ 03 no hubo un requisito estricto para obtener el valor de ++i directamente de i . El nuevo valor podría haber sido predicho / precalculado como i + 1 y utilizado como un operando de binario + incluso antes de que se almacenara físicamente en el i real. Aunque se puede afirmar de manera razonable que el requisito estaba implícitamente incluido en C ++ 03 (y el hecho de que C ++ 03 no reconoció su existencia fue un defecto de C ++ 03)


En el caso de i++ , el resultado es un valor r . Dado que ya es un valor r, no hay conversión de valor l a valor de cambio involucrado y no hay absolutamente ningún requisito de almacenarlo en i antes de comenzar a evaluar el binario.

Hace mucho tiempo que hice esta pregunta, escribí un artículo sobre la visualización de la secuencia de evaluación de C ++ con gráficos . Esta pregunta es idéntica al caso de i = ++i , que tiene el siguiente gráfico secuenciado antes:

Gráfico secuencial antes de i = ++ i

Los nodos rojos representan efectos secundarios en i . El nodo azul representa una evaluación que usa el valor de i . La razón por la cual esta expresión está bien definida es porque no hay ninguno de estos nodos no secuenciados entre sí. El uso de i a la izquierda de la asignación no usa el valor de i , por lo que no es un problema.

La parte principal que me faltaba era lo que significa “usa el valor”. Un operador usa el valor de su operando si espera una expresión de prvalue para ese operando. Al dar el nombre de un objeto, que es un valor l, el lvalue debe pasar por una conversión de valor a valor r, que se puede ver como “leer el valor del objeto”. La asignación solo requiere un valor l para su operando izquierdo, lo que significa que no usa su valor.