una teclas tabulador tabla son secuencias secuencia saldo representa qué que para nueva línea los lenguaje juego escape definicion cuáles caracteres c++ language-lawyer side-effects sequence-points

c++ - teclas - tabla de secuencia de escape lenguaje c



Cálculos de valor sin secuencia(también conocidos como puntos de secuencia) (2)

Lo siento por volver a abrir este tema, pero pensar en este tema en sí mismo ha comenzado a darme un comportamiento indefinido. Quiere moverse en la zona de comportamiento bien definido.

Dado

int i = 0; int v[10]; i = ++i; //Expr1 i = i++; //Expr2 ++ ++i; //Expr3 i = v[i++]; //Expr4

Pienso en las expresiones anteriores (en ese orden) como

operator=(i, operator++(i)) ; //Expr1 equivalent operator=(i, operator++(i, 0)) ; //Expr2 equivalent operator++(operator++(i)) ; //Expr3 equivalent operator=(i, operator[](operator++(i, 0)); //Expr4 equivalent

Ahora que vienen a los comportamientos aquí están las citas importantes de C ++ 0x .

$ 1.9 / 12- "La evaluación de una expresión (o una subexpresión) en general incluye ambos cálculos de valores (incluida la determinación de la identidad de un objeto para la evaluación del valor l y el valor de fetchinga asignado previamente a un objeto para la evaluación del valor r) y el inicio de los efectos secundarios . "

$ 1.9 / 15- "Si un efecto secundario en un objeto escalar no se secuencia en relación con otro efecto secundario en el mismo objeto escalar o un cálculo de valor utilizando el valor del mismo objeto escalar, el comportamiento no está definido".

[Nota: los cálculos de valor y los efectos secundarios asociados con diferentes expresiones de argumentos no tienen secuencia. "Nota final"

$ 3.9 / 9- "Los tipos aritméticos (3.9.1), los tipos de enumeración, los tipos de puntero, los tipos de puntero a miembro (3.9.2), std :: nullptr_t, y las versiones calificadas por CV de estos tipos (3.9.3) se llaman colectivamente tipos escalares ".

  • En Expr1, la evaluación de la expresión i (primer argumento) no tiene secuencia con respecto a la evaluación del operator++(i) de la sesión operator++(i) (que tiene un efecto secundario).

    Por lo tanto, Expr1 tiene un comportamiento indefinido.

  • En Expr2, la evaluación de la expresión i (primer argumento) no tiene secuencia con respecto a la evaluación del operator++(i, 0) de la sesión operator++(i, 0) (que tiene un efecto secundario) ''.

    Por lo tanto, Expr2 tiene un comportamiento indefinido.

  • En Expr3, la evaluación del operator++(i) argumento solitario operator++(i) debe completarse antes de llamar al operator++ externo operator++ .

    Por eso Expr3 tiene un comportamiento bien definido.

  • En Expr4, la evaluación de la expresión i (primer argumento) no tiene secuencia con respecto a la evaluación del operator[](operator++(i, 0) (que tiene un efecto secundario).

    Por lo tanto, Expr4 tiene un comportamiento indefinido.

¿Es correcto este entendimiento?

PS El método de analizar las expresiones como en OP no es correcto. Esto se debe a que, como @Potatoswatter, señala - "la cláusula 13.6 no se aplica. Consulte la cláusula de exención de responsabilidad en 13.6 / 1," Estas funciones candidatas participan en el proceso de resolución de sobrecarga del operador como se describe en 13.3.1.2 y no se utilizan para ningún otro propósito. "Solo son declaraciones ficticias; no existe una semántica de llamada de función con respecto a los operadores integrados".


Al pensar en expresiones como las que se mencionan, me parece útil imaginar una máquina donde la memoria tiene interbloqueos, de modo que la lectura de una ubicación de la memoria como parte de una secuencia de lectura-modificación-escritura causará cualquier intento de lectura o escritura, aparte de la escritura final. La secuencia, para ser detenida hasta que la secuencia se complete. Tal máquina difícilmente sería un concepto absurdo; de hecho, tal diseño podría simplificar muchos escenarios de código de subprocesos múltiples. Por otro lado, una expresión como "x = y ++;" podría fallar en tal máquina si ''x'' e ''y'' fueran referencias a la misma variable, y el código generado por el compilador hizo algo como leer y bloquear reg1 = y; reg2 = reg1 + 1; escribe x = reg1; escribir y desbloquear y = reg2. Esa sería una secuencia de código muy razonable en los procesadores donde la escritura de un valor recientemente calculado impondría un retraso en la tubería, pero la escritura en x bloquearía el procesador si y fuera un alias de la misma variable.


Las expresiones de operador nativas no son equivalentes a las expresiones de operador sobrecargadas. Hay un punto de secuencia en el enlace de valores a argumentos de función, lo que hace que las versiones del operator++() estén bien definidas. Pero eso no existe para el caso de tipo nativo.

En los cuatro casos, i cambia dos veces dentro de la expresión completa. Como no || , o && aparecen en las expresiones, eso es instantáneo UB.

§5 / 4:

Entre el punto de secuencia anterior y el siguiente, un objeto escalar tendrá su valor almacenado modificado a lo sumo una vez por la evaluación de una expresión.

Editar para C ++ 0x (actualizado)

§1.9 / 15:

Los cálculos de valores de los operandos de un operador se secuencian antes del cálculo de valores del resultado del operador. Si un efecto secundario en un objeto escalar no tiene secuencia en relación con otro efecto secundario en el mismo objeto escalar o un cálculo de valor utilizando el valor del mismo objeto escalar, el comportamiento es indefinido.

Sin embargo, tenga en cuenta que un cálculo de valor y un efecto secundario son dos cosas distintas. Si ++i es equivalente a i = i+1 , entonces + es el cálculo del valor y = es el efecto secundario. Desde el 1.9 / 12:

La evaluación de una expresión (o una subexpresión) en general incluye tanto los cálculos de valores (incluida la determinación de la identidad de un objeto para la evaluación de glvalue como la obtención de un valor previamente asignado a un objeto para la evaluación de prvalue) y el inicio de los efectos secundarios.

Entonces, aunque los cálculos de valores están más fuertemente secuenciados en C ++ 0x que en C ++ 03, los efectos secundarios no lo son. Dos efectos secundarios en la misma expresión, a menos que se secuencian de otra manera, producen UB.

Los cálculos de valor están ordenados por sus dependencias de datos de todos modos y, a falta de efectos secundarios, su orden de evaluación es inobservable, por lo que no estoy seguro de por qué C ++ 0x se toma la molestia de decir algo, pero eso solo significa que necesito leer más De los papeles de Boehm y amigos escribieron.

Edición # 3:

Gracias Johannes por hacer frente a mi pereza al escribir "secuenciado" en la barra de búsqueda de mi lector de PDF. Me iba a la cama y me levanté en las dos últimas ediciones de todos modos ... a la derecha; v).

§5.17 / 1 que define los operadores de asignación dice

En todos los casos, la asignación se secuencia después del cálculo del valor de los operandos derecho e izquierdo, y antes del cálculo del valor de la expresión de asignación.

También el §5.3.2 / 1 en el operador de preincremento dice

Si x no es de tipo bool, la expresión ++ x es equivalente a x + = 1 [Nota: vea ... suma (5.7) y operadores de asignación (5.17) ...].

Por esta identidad, ++ ++ x es una abreviatura de (x +=1) +=1 . Entonces, interpretemos eso.

  • Evalúa el 1 en el extremo derecho y desciende a los parens.
  • Evalúe el 1 interno y el valor (prvalue) y la dirección (glvalue) de x .
  • Ahora necesitamos el valor de la subexpresión + =.
    • Hemos terminado con los cálculos de valores para esa subexpresión.
    • ¡El efecto secundario de la asignación debe secuenciarse antes de que el valor de la asignación esté disponible!
  • Asigne el nuevo valor a x , que es idéntico al valor de glvalue y prvalue resultante de la subexpresión.
  • Estamos fuera de peligro ahora. La expresión completa ahora se ha reducido a x +=1 .

Entonces, 1 y 3 están bien definidos y 2 y 4 son comportamientos indefinidos, lo que cabría esperar.

La única otra sorpresa que encontré al buscar "secuenciada" en N3126 fue 5.3.4 / 16, donde se permite que la implementación llame al operator new antes de evaluar los argumentos del constructor. Eso es genial.

Edición # 4: (Oh, qué red enredada tejemos)

Johannes vuelve a notar que en i == ++i; el valor de glvalue (también conocido como la dirección) de i es ambiguamente dependiente de ++i . El valor glico es ciertamente un valor de i , pero no creo que 1.9 / 15 tenga la intención de incluirlo por la sencilla razón de que el valor líquido de un objeto con nombre es constante, y en realidad no puede tener dependencias.

Para un hombre de paja informativo, considere

( i % 2? i : j ) = ++ i; // certainly undefined

En este caso, el valor de la LHS de = depende de un efecto secundario sobre el valor de i . La dirección de i no está en cuestión; El resultado de la ?: es.

Quizás un buen contraejemplo sea

int i = 3, &j = i; j = ++ i;

Aquí j tiene un glvalue distinto de (pero idéntico a) i . ¿Está bien definido, pero i = ++i no lo está? Esto representa una transformación trivial que un compilador podría aplicar a cualquier caso.

1.9 / 15 debería decir

Si un efecto secundario en un objeto escalar no tiene secuencia en relación con otro efecto secundario en el mismo objeto escalar o un cálculo de valor utilizando el prvalor del mismo objeto escalar, el comportamiento no está definido.