Изменился ли стандарт C++ относительно использования неопределенных значений и неопределенного поведения в C++14?

Как описано в разделе "Включает ли инициализация преобразование lvalue в rvalue"? Является int x = x; UB? стандарт C++ имеет удивительный пример в разделе 3.3.2 Точка декларации, в которой int инициализируется своим собственным неопределенным значением:

int x = 12;
{ int x = x; }

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

То, на что Йоханнес отвечает на этот вопрос, является неопределенным поведением, так как требует преобразования lvalue в rvalue.

В последнем проекте стандарта C++14 N3936 который можно найти здесь, этот пример изменился на:

unsigned char x = 12;
{ unsigned char x = x; }

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

Что-то изменилось в C++14 в отношении неопределенных значений и неопределенного поведения, которое послужило причиной этого изменения в примере?

1 ответ

Решение

Да, это изменение было вызвано изменениями в языке, что делает его неопределенным поведением, если в результате оценки получено неопределенное значение, но с некоторыми исключениями для неподписанных узких символов.

Отчет о дефектах 1787 чей предложенный текст можно найти в N3914 1, был недавно принят в 2014 году и включен в последний рабочий проект N3936:

Наиболее интересным изменением в отношении неопределенных значений будет раздел 8.5 пункт 12, который идет от:

Если для объекта не указан инициализатор, объект инициализируется по умолчанию; если инициализация не выполняется, объект с автоматическим или динамическим сроком хранения имеет неопределенное значение. [ Примечание: объекты со статическим или потоковым хранением инициализируются нулями, см. 3.6.2. - конец примечания ]

чтобы (выделение мое):

Если для объекта не указан инициализатор, объект инициализируется по умолчанию. При получении хранилища для объекта с автоматической или динамической продолжительностью хранения объект имеет неопределенное значение, и если для объекта не выполняется инициализация, этот объект сохраняет неопределенное значение до тех пор, пока это значение не будет заменено (5.17 [expr.ass]), [Примечание: объекты со статическим или потоковым хранилищем инициализируются нулями, см. 3.6.2 [basic.start.init]. - примечание] Если в результате оценки получено неопределенное значение, поведение не определено, за исключением следующих случаев:

  • Если неопределенное значение беззнакового типа узкого символа (3.9.1 [basic.fundamental]) получается путем оценки:

    • второй или третий операнд условного выражения (5.16 [expr.cond]),

    • правый операнд запятой (5.18 [expr.comma]),

    • операнд приведения или преобразования в беззнаковый узкий символьный тип (4.7 [conv.integral], 5.2.3 [expr.type.conv], 5.2.9 [expr.static.cast], 5.4 [expr.cast]), или же

    • выражение отброшенного значения (раздел 5 [expr]),

    тогда результат операции является неопределенным значением.

  • Если неопределенное значение типа узких символов без знака (3.9.1 [basic.fundamental]) создается путем вычисления правого операнда простого оператора присваивания (5.17 [expr.ass]), первый операнд которого является lvalue узкого знака без знака тип символа, неопределенное значение заменяет значение объекта, на которое ссылается левый операнд.

  • Если неопределенное значение узкого символьного типа без знака (3.9.1 [basic.fundamental]) создается путем вычисления выражения инициализации при инициализации объекта узкого символьного типа без знака, этот объект инициализируется неопределенным значением.

и включил следующий пример:

[ Пример:

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
}

- конец примера ]

Мы можем найти этот текст в N3936, который является текущим рабочим проектом и N3937 это C++14 DIS,

До C++1й

Интересно отметить, что до этого проекта в отличие от C, который всегда имел четко определенное представление о том, какие использования неопределенных значений были неопределенными, C++ использовал термин неопределенное значение, даже не определяя его ( предполагая, что мы не можем заимствовать определение из C99), а также см. отчет о дефекте 616. Мы должны были полагаться на недостаточно конкретное преобразование lvalue в rvalue, которое в проекте стандарта C++11 рассматривается в разделе 4.1 Пункт 1 преобразования lvalue в rvalue, в котором говорится:

[...] если объект не инициализирован, программа, которая требует этого преобразования, имеет неопределенное поведение.[...]


Примечания:

  1. 1787 это версия отчета о дефекте 616, мы можем найти эту информацию в N3903
Другие вопросы по тегам