Во всех версиях 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?