versiones tipos programacion principales librerias lenguaje datos caracteristicas c++ bit-shift c++14

tipos - ¿Por qué se cambió 1<< 31 para ser definido en la implementación en C++ 14?



tipos de datos en c++ (1)

En todas las versiones de C y C ++ anteriores a 2014, escribir.

1 << (CHAR_BIT * sizeof(int) - 1)

causó un comportamiento indefinido, porque el desplazamiento a la izquierda se define como equivalente a la multiplicación sucesiva por 2 , y este desplazamiento causa un desbordamiento de enteros con signo:

El resultado de E1 << E2 es E1 posiciones de bit E2 desplazadas a la izquierda; bits vacíos están llenos de ceros. [...] Si E1 tiene un tipo con signo y un valor no negativo, y E1 × 2 E2 es representable en el tipo de resultado, ese es el valor resultante; de lo contrario, el comportamiento es indefinido .

Sin embargo, en C ++ 14 el texto ha cambiado para << pero no para la multiplicación:

El valor de E1 << E2 es E1 posiciones de bit E2 desplazadas a la izquierda; los bits vacíos se rellenan con cero. [...] De lo contrario, si E1 tiene un tipo con signo y un valor no negativo, y E1 × 2 E2 se puede representar en el tipo sin signo correspondiente del tipo de resultado, entonces ese valor, convertido al tipo de resultado , es el valor resultante ; de lo contrario, el comportamiento es indefinido.

El comportamiento es ahora el mismo que para la asignación fuera de rango al tipo firmado, es decir, como está cubierto por [conv.integral] / 3:

Si el tipo de destino está firmado, el valor no se modifica si se puede representar en el tipo de destino (y el ancho del campo de bits); de lo contrario, el valor está definido por la implementación .

Esto significa que aún no es portátil escribir 1 << 31 (en un sistema con 32 bits int). Entonces, ¿por qué se hizo este cambio en C ++ 14?


El problema relevante es CWG 1457 , donde la justificación es que el cambio permite que 1 << 31 se use en expresiones constantes:

La redacción actual del párrafo 5 [expr.shift] 5 hace que sea un comportamiento indefinido crear el entero más negativo de un tipo dado al desplazar a la izquierda un (firmado) 1 en el bit de signo, aunque esto no se realiza de forma infrecuente y funciona correctamente en la mayoría de las arquitecturas (dos complementos):

... si E1 tiene un tipo con signo y un valor no negativo, y E1 * 2 E2 es representable en el tipo de resultado, ese es el valor resultante; de lo contrario, el comportamiento es indefinido.

Como resultado, esta técnica no se puede utilizar en una expresión constante, que romperá una cantidad significativa de código.

Las expresiones constantes no pueden contener un comportamiento indefinido, lo que significa que el uso de una expresión que contiene UB en un contexto que requiere una expresión constante hace que el programa no esté bien formado. libstdc ++ ''s numeric_limits::min , por ejemplo, una vez no pudo compilar en clang debido a esto.