Изменился ли стандарт 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, в котором говорится:
[...] если объект не инициализирован, программа, которая требует этого преобразования, имеет неопределенное поведение.[...]
Примечания:
1787
это версия отчета о дефекте 616, мы можем найти эту информацию в N3903