¿Por qué se cambió 1 <31 para que se definiera como implementación en C++14?


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

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 desbordamiento de enteros con signo:

El resultado de E1 << E2 es E1 posiciones de bits cambiadas a la izquierda E2; los bits vacíos se rellenan con ceros. [...] Si E1 tiene un tipo con signo y un valor no negativo, y E1 × 2E2 es representable en el tipo de resultado, entonces 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 bits cambiadas a la izquierda E2; los bits vacíos están llenos de cero. [...] De lo contrario, si E1 tiene un tipo con signo y un valor no negativo, y E1 × 2E2 es representable en el tipo sin signo correspondiente del tipo 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 cambia 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 todavía no portable para escribir 1 << 31 (en un sistema con int de 32 bits). Entonces, ¿por qué se hizo este cambio en C++14?

Author: M.M, 2014-10-13

1 answers

La cuestión relevante es CWG 1457 , donde la justificación es que el cambio permite 1 << 31 para ser utilizado en expresiones constantes:

La redacción actual de 5.8 [expr.shift] el párrafo 2 lo hace indefinido comportamiento para crear el entero más negativo de un tipo dado por izquierda-desplazando un (firmado) 1 en el bit de signo, aunque esto no es hecho poco común y funciona correctamente en la mayoría de (doses-complement) arquitecturas:

...si E1 tiene firmado tipo y valor no negativo, y E1 * 2E2 es representable en el tipo de resultado, entonces 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á un cantidad significativa de código.

Las expresiones constantes no pueden contener un comportamiento indefinido, lo que significa que usar una expresión que contenga UB en un contexto que requiera una expresión constante hace que el programa esté mal formado. libstdc++numeric_limits::min, por ejemplo, una vez no pudo compilar en clang debido a esto.

 18
Author: T.C.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-12 23:45:54