Como se trata en ¿La inicialización implica la conversión de lvalue a rvalue? Es int x = x;
UB? el estándar C++ tiene un ejemplo sorprendente en la sección 3.3.2
Punto de declaración en el que un int
se inicializa con su propio valor indeterminado:
int x = 12;
{ int x = x; }
Aquí la segunda x se inicializa con su propio valor (indeterminado). — ejemplo final ]
Lo que indica la respuesta de Johannes a esta pregunta es un comportamiento indefinido, ya que requiere una conversión de lvalue a rvalue.
En el último borrador del estándar C++14 N3936
que se puede encontrar aquí este ejemplo ha cambiado a:
unsigned char x = 12;
{ unsigned char x = x; }
Aquí la segunda x se inicializa con su propio valor (indeterminado). — ejemplo final ]
¿Ha cambiado algo en C++14 con respecto a los valores indeterminados y el comportamiento indefinido que ha impulsado este cambio en el ejemplo?
Sí, este cambio fue impulsado por cambios en el idioma, lo que lo convierte en un comportamiento indefinido. si un valor indeterminado es producido por una evaluación pero con algunas excepciones para caracteres estrechos sin firmar.
Informe de defectos 1787 cuyo texto propuesto se encuentra en N39141 era recientemente aceptado en 2014 y está incorporado en el último borrador de trabajo N3936
:
El cambio más interesante con respecto a los valores indeterminados sería la sección 8.5
párrafo 12 que va de:
Si no se especifica un inicializador para un objeto, el objeto se inicializa por defecto; si no se realiza ninguna inicialización, un objeto con duración de almacenamiento automático o dinámico tiene un valor indeterminado. [ Note: Objects with static or thread storage duration are zero-initialized, see 3.6.2. — end note ]
para (énfasis mío):
Si no se especifica ningún inicializador para un objeto, el objeto se inicializa de forma predeterminada. Cuando se obtiene almacenamiento para un objeto con duración de almacenamiento automático o dinámico, el objeto tiene una valor indeterminadoy si no se realiza ninguna inicialización para el objeto, ese objeto retiene un valor indeterminado hasta que se reemplaza ese valor (5.17 [expr.ass]). [Note: Objects with static or thread storage
duration are zero-initialized, see 3.6.2 [basic.start.init]. —nota final] Si una evaluación produce un valor indeterminado, el comportamiento es indefinido excepto en los siguientes casos:
-
Si un valor indeterminado de tipo de carácter estrecho sin signo (3.9.1 [basic.fundamental]) se produce por la evaluación de:
-
el segundo o tercer operando de una expresión condicional (5.16 [expr.cond]),
-
el operando derecho de una coma (5.18 [expr.comma]),
-
el operando de una conversión o conversión a un tipo de carácter estrecho sin signo (4.7 [conv.integral]5.2.3 [expr.type.conv]5.2.9
[expr.static.cast]5.4 [expr.cast]), o
-
una expresión de valor descartado (Cláusula 5 [expr]),
entonces el resultado de la operación es un valor indeterminado.
-
Si un valor indeterminado de tipo de carácter estrecho sin signo (3.9.1 [basic.fundamental]) es producido por la evaluación del operando derecho de un operador de asignación simple (5.17 [expr.ass]) cuyo primer operando es un valor l de tipo de carácter estrecho sin signo, un valor indeterminado reemplaza el valor del objeto al que hace referencia el operando izquierdo.
-
Si un valor indeterminado de tipo de carácter estrecho sin signo (3.9.1 [basic.fundamental]) es producido por la evaluación de la expresión de inicialización cuando se inicializa un objeto de tipo de carácter estrecho sin signo, ese objeto se inicializa a un valor indeterminado.
e incluyó el siguiente ejemplo:
[ Example:
int f(bool b) {
unsigned char c;
unsigned char d = c; // OK, d has an indeterminate value
int e = d; // undefined behavior
return b ? d : 0; // undefined behavior if b is true
}
— end example ]
Podemos encontrar este texto en N3936 cual es la corriente borrador de trabajo y N3937
es el C++14 DIS
.
Antes de C++1y
Es interesante notar que antes de este borrador, a diferencia de C que siempre ha tenido una noción bien especificada de qué usos de los valores indeterminados eran indefinidos C++ usó el término valor indeterminado sin siquiera definirlo (asumiendo que no podemos tomar prestada la definición de C99) y también ver informe de defectos 616. Tuvimos que confiar en la conversión de lvalue a rvalue no especificada que, en borrador del estándar C++11 está cubierto en la sección 4.1
Conversión de valor L a valor R párrafo 1 que dice:
[…]si el objeto no está inicializado, un programa que necesita esta conversión tiene un comportamiento indefinido.[…]
Notas al pie:
1787
es una revisión de informe de defectos 616podemos encontrar esa información en N3903
Papel relevante: Por qué nada importa: el impacto de la reducción a cero cuando surge la pregunta, ¿por qué no simplemente poner a cero la memoria no inicializada?
– Shafik Yaghmour
18 de diciembre de 2017 a las 18:29
Si bien el documento es interesante, sus conclusiones no se aplican necesariamente a un lenguaje compilado con anticipación donde los análisis estáticos podrían eliminar la mayor parte o la totalidad del costo.
– davmac
19/03/2018 a las 13:30
@davmac Este es un ejemplo más práctico. y todos los demás artículos que encontré sobre esto encontraron costos similares. No creo que se haya probado que podamos eliminar este costo, aunque tal vez sea posible.
– Shafik Yaghmour
19 de marzo de 2018 a las 16:31
Ejemplo que muestra cómo se puede eliminar definitivamente el costo en un caso trivial: godbolt.org/g/Kh9xsp – Estoy de acuerdo en que ciertamente no siempre será posible/práctico eliminar todo el costo, pero ciertamente se ha demostrado que los compiladores pueden eliminar el costo en al menos algunos casos, y no parece haber números difíciles que intenten hacerlo. evalúe el costo promedio/potencial de un compilador AOT optimizado, que es mi punto principal.
– davmac
19 de marzo de 2018 a las 16:55