Uso de variable en inicializador propio

22

[basic.scope.pdecl] / 1 del borrador estándar de C ++ 20 tenía el siguiente ejemplo (no normativo) en una nota (cita parcial anterior a la fusión de la solicitud de extracción 3580 , consulte la respuesta a esta pregunta):

unsigned char x = x;

[...] x se inicializa con su propio valor (indeterminado).

¿Esto realmente tiene un comportamiento bien definido en C ++ 20?


En general, la autoinicialización de la forma T x = x;tiene un comportamiento indefinido en virtud de que xel valor de este es indeterminado antes de que se complete la inicialización. La evaluación de valores indeterminados generalmente causa un comportamiento indefinido ( [basic.indent] / 2 ), pero hay una excepción específica en [basic.indent] /2.3 que permite inicializar directamente una unsigned charvariable desde un unsigned charvalor l con valor indeterminado (causando la inicialización con un valor indeterminado )

Por lo tanto, esto por sí solo no causa un comportamiento indefinido, pero lo haría para otros tipos Tque no son tipos de caracteres estrechos sin signo o std::byte, por ejemplo int x = x;. Estas consideraciones también se aplicaron en C ++ 17 y antes, consulte también las preguntas vinculadas en la parte inferior.

Sin embargo, incluso para unsigned char x = x;, el borrador actual [basic.lifetime] / 7 dice:

De manera similar, antes de que la vida útil de un objeto haya comenzado [...] a usar las propiedades del valor gl que no dependen de su valor está bien definido. El programa tiene un comportamiento indefinido si:

  • el valor gl se usa para acceder al objeto, o

  • [...]

Esto parece implicar que xel valor en el ejemplo solo puede usarse durante su vida útil.

[basic.lifetime] / 1 dice:

[...]

La vida útil de un objeto de tipo T comienza cuando:

  • [...] y
  • su inicialización (si la hay) está completa (incluida la inicialización vacía) ([dcl.init]),

[...]

Así x, la vida útil comienza solo después de que se completa la inicialización. Pero en el ejemplo citado x, el valor se usa antes de que xse complete la inicialización. Por lo tanto, el uso tiene un comportamiento indefinido.

¿Es correcto mi análisis? Si es así, ¿afecta a casos similares de uso antes de la inicialización, como

int x = (x = 1);

¿cuál, por lo que puedo decir, estaba bien definido en C ++ 17 y antes también?


Tenga en cuenta que en C ++ 17 (borrador final) el segundo requisito para que comience la vida útil fue diferente :

  • si el objeto tiene una inicialización no vacía, su inicialización está completa,

Dado que xtendría una inicialización vacía por la definición de C ++ 17 (pero no la del borrador actual), su vida útil ya habría comenzado cuando se accede al inicializador en los ejemplos dados anteriormente, por lo que en ambos ejemplos no hubo un comportamiento indefinido debido a la vida útil de xen C ++ 17.

La redacción antes de C ++ 17 es nuevamente diferente, pero con el mismo resultado.


La pregunta no es sobre el comportamiento indefinido cuando se usan valores indeterminados, que se cubrieron, por ejemplo, en las siguientes preguntas:

nuez
fuente
@LanguageLawyer No estoy seguro de estar en lo cierto, especialmente si nadie ha respondido todavía. Si otros van a estar de acuerdo conmigo aquí, podría presentar uno más tarde (o tal vez alguien más lo hará antes que yo), pero no quiero presentar problemas de los que no estoy seguro.
nogal
@LanguageLawyer: No puede ser un problema editorial si el documento de trabajo dice inequívocamente lo incorrecto.
Davis Herring
1
La palabra es cambiada por P1358 .
xskxzr
1
@xskxzr Correcto, y mientras tanto LanguageLawyer también presentó un problema editorial , que parece haber sido enviado a CWG para aclarar su intención.
nogal
1
@ clockw0rk int x ^= x;no está sintácticamente bien formado. Puede tener una definición variable con inicializador (es decir int x = x;, aunque sea UB) o una declaración de expresión de asignación xor (es decir x ^= x;, aunque es UB si xes de tipo int, se inicializó por defecto y no se asignó de antemano). No puedes mezclar estos dos en uno.
nuez

Respuestas:

8

Esto se abrió como un problema editorial . Se envió al CWG para su discusión (interna). Aproximadamente 24 horas después, la persona que envió el problema creó una solicitud de extracción que modifica el ejemplo para dejar en claro que esto es UB:

Aquí, la inicialización del segundo \ tcode {x} tiene un comportamiento indefinido, porque el inicializador accede al segundo \ tcode {x} fuera de su tiempo de vida \ iref {basic.life}.

Desde entonces, se agregó ese RP y se cerró el problema. Por lo tanto, parece claro que la interpretación obvia (UB debido al acceso a un objeto cuya vida útil no ha comenzado) es la interpretación prevista. Parece que la intención del comité es hacer que estas construcciones no funcionen, y el texto no normativo de la norma ha sido actualizado para reflejar esto.

Nicol Bolas
fuente