Использование переменной в собственном инициализаторе

22

[basic.scope.pdecl] / 1 стандартного черновика C ++ 20 содержал в примечании следующий (ненормативный) пример (частичная цитата до запроса на объединение 3580 , см. ответ на этот вопрос):

unsigned char x = x;

[...] x инициализируется своим собственным (неопределенным) значением.

Это на самом деле имеет четко определенное поведение в C ++ 20?


Обычно самоинициализация формы T x = x;имеет неопределенное поведение в силу неопределенностиx значения до завершения инициализации. Оценка неопределенных значений обычно вызывает неопределенное поведение ( [basic.indent] / 2 ), но в [basic.indent] /2.3 есть специальное исключение, которое позволяет напрямую инициализировать переменную из lvalue с неопределенным значением (вызывая инициализацию с неопределенным значением) ).unsigned charunsigned char

Это само по себе, следовательно, не вызывает неопределенного поведения, но будет для других типов T, которые не являются беззнаковыми узкими символьными типами или std::byte, например int x = x;. Эти соображения применяются в C ++ 17 и ранее, см. Также связанные вопросы внизу.

Однако даже для unsigned char x = x;текущего проекта [basic.lifetime] / 7 говорится:

Точно так же до того, как время жизни объекта началось [...], свойства glvalue, которые не зависят от его значения, четко определены. Программа имеет неопределенное поведение, если:

  • glvalue используется для доступа к объекту, или

  • [...]

Кажется, это подразумевает, что xзначение в примере может использоваться только в течение его времени жизни.

[basic.lifetime] / 1 говорит:

[...]

Время жизни объекта типа T начинается, когда:

  • [...] а также
  • его инициализация (если есть) завершена (включая пустую инициализацию) ([dcl.init]),

[...]

Таким образом x, время жизни начинается только после завершения инициализации. Но в приведенном примере xзначение используется до xзавершения инициализации. Поэтому использование имеет неопределенное поведение.

Является ли мой анализ правильным и влияет ли он на подобные случаи использования перед инициализацией, такие как

int x = (x = 1);

которые, насколько я могу судить, были четко определены в C ++ 17 и ранее?


Обратите внимание, что в C ++ 17 (окончательный вариант) второе требование для начала жизни было другим :

  • если объект имеет не пустую инициализацию, его инициализация завершена,

Так xкак инициализация была бы бессодержательной по определению C ++ 17 (но не по текущему проекту), его время жизни уже началось бы, когда к нему обращались в инициализаторе в приведенных выше примерах, и поэтому в обоих примерах не было неопределенного поведения из-за времени жизни xв C ++ 17.

Формулировка до C ++ 17 снова отличается, но с тем же результатом.


Вопрос не о неопределенном поведении при использовании неопределенных значений, который был рассмотрен, например, в следующих вопросах:

грецкий орех
источник
@LanguageLawyer Я не уверен, что я прав, особенно если никто еще не ответил. Если другие согласятся со мной здесь, я мог бы подать один позже (или, может быть, кто-то еще будет до меня), но я не хочу подавать вопросы, в которых я не уверен.
грецкий орех
@LanguageLawyer: Это не может быть редакционной проблемой, если рабочий документ недвусмысленно говорит не то, что нужно.
Дэвис Херринг
1
Слово изменено P1358 .
xskxzr
1
@xskxzr Верно, а тем временем LanguageLawyer также подал редакционную проблему , которая, похоже, была передана в CWG для разъяснения намерения.
орех
1
@ clockw0rk int x ^= x;не является синтаксически правильно сформированным. Вы можете иметь определение переменной с инициализатором (т. int x = x;Е. Хотя это UB) или оператор выражения присваивания xor (т. x ^= x;Е. Хотя это UB, если он xимеет тип int, инициализирован по умолчанию и не назначен заранее). Вы не можете смешать эти два в одно.
грецкий орех

Ответы:

8

Это было открыто как редакционная проблема . Он был направлен в CWG для (внутреннего) обсуждения. Примерно через 24 часа человек, который направил проблему, создал запрос на извлечение, который модифицирует пример, чтобы прояснить, что это UB:

Здесь инициализация второго \ tcode {x} имеет неопределенное поведение, поскольку инициализатор получает доступ ко второму \ tcode {x} вне его времени жизни \ iref {basic.life}.

Этот PR был добавлен, и проблема закрыта. Таким образом, кажется очевидным, что очевидная интерпретация (UB из-за доступа к объекту, время жизни которого еще не началось) является предполагаемой интерпретацией. Похоже, что целью комитета является сделать эти конструкции не функциональными, и ненормативный текст стандарта был обновлен, чтобы отразить это.

Николь Болас
источник