Почему в версии С++ 14 было изменено 1 << 31?
Во всех версиях C и С++ до 2014 года запись
1 << (CHAR_BIT * sizeof(int) - 1)
вызвало поведение undefined, поскольку сдвиг слева определяется как эквивалентный последовательному умножению на 2
, и этот сдвиг вызывает сплошное переполнение цепочки:
Результатом E1 << E2
является E1
левое смещение E2
битовых позиций; освобожденные биты заполняются нулями. [...] Если E1
имеет подписанный тип и неотрицательное значение, а E1
× 2 E2 представляется в типе результата, то это результирующее значение; в противном случае поведение undefined.
Однако в С++ 14 текст изменился для <<
, но не для умножения:
Значение E1 << E2
- это E1
сдвинутые слева позиции E2
; освобожденные биты заполняются нулями. [...] В противном случае, если E1
имеет подписанный тип и неотрицательное значение, а E1
× 2 E2 представляется в соответствующем неподписанном типе тип результата, то это значение, , преобразованное в тип результата, является результирующим значением; в противном случае поведение undefined.
Поведение теперь такое же, как для присвоения вне диапазона для подписанного типа, т.е. как описано в [conv.integral]/3:
Если тип назначения подписан, значение не изменяется, если оно может быть представлено в типе назначения (и ширине битового поля); в противном случае значение определяется реализацией.
Это означает, что он все еще не переносится для записи 1 << 31
(в системе с 32-битным int). Итак, почему это изменение было внесено в С++ 14?
Ответы
Ответ 1
Соответствующая проблема CWG 1457, где обоснование заключается в том, что изменение позволяет использовать 1 << 31
в постоянных выражениях:
Текущая редакция параграфа 5.8 [expr.shift] 2 делает ее undefinedповедение, чтобы создать наиболее отрицательное целое число данного типа посредством сдвиг слева (1) в знаковый бит, хотя это не необычно сделано и работает правильно на большинстве (двухкомпонентные) архитектуры:
... если E1 имеет подписанный тип и неотрицательное значение, а E1 * 2 E2 - представляемый в типе результата, то это результирующее значение; в противном случае поведение undefined.
В результате этот метод не может использоваться в постоянном выражении, которое нарушит значительный объем кода.
Константные выражения не могут содержать поведение undefined, что означает, что использование выражения, содержащего UB в контексте, требующем постоянного выражения, делает программу плохо сформированной. libstdС++ numeric_limits::min
, например, после этого не удалось скомпилировать в clang.