c++ language-lawyer c++17 cin c++-standard-library

c++ - ¿Debería fallar la lectura de negativo en unsigned a través de std:: cin(gcc, clang, en desacuerdo)?



language-lawyer c++17 (2)

Creo que ambos están mal en C ++ 17 1 y que el resultado esperado debería ser:

4294967295 0

Esto es, sin embargo, difícil de entender desde el estándar ...

El estándar dice - [facet.num.get.virtuals#3.3] :

La secuencia de caracteres acumulados en la etapa 2 (el campo) se convierte a un valor numérico mediante las reglas de una de las funciones declaradas en el encabezado <cstdlib> :

  • Para un valor entero con signo, la función strtoll .

  • Para un valor entero sin signo, la función strtoull .

  • Para un valor de punto flotante, la función strtold .

Así que recurrimos a std::strtoull , que debe devolver 2 ULLONG_MAX y no establecer errno en este caso (que es lo que hacen ambos compiladores).

Pero en el mismo bloque (el énfasis es mío):

El valor numérico que se almacenará puede ser uno de los siguientes:

  • cero, si la función de conversión no convierte todo el campo.

  • el valor representable más positivo (o negativo), si el campo que se va a convertir a un tipo entero con signo representa un valor positivo (o negativo) demasiado grande para ser representado en val .

  • el valor representable más positivo, si el campo que se va a convertir a un tipo entero sin signo representa un valor que no se puede representar en val .

  • el valor convertido, de lo contrario.

El valor numérico resultante se almacena en val . Si la función de conversión no convierte el campo completo, o si el campo representa un valor fuera del rango de valores representables, ios_base​::​failbit se asigna a err .

Tenga en cuenta que todas estas conversaciones sobre el "campo a convertir" y no el valor real devuelto por std::strtoull . El campo aquí es en realidad la secuencia ampliada del carácter ''-'', ''1'' .

Dado que el campo representa un valor (-1) que no puede ser representado por un unsigned , el valor devuelto debe ser UINT_MAX y el failbit debe establecerse en std::cin .

1 clang realidad era correcto antes de C ++ 17 porque la tercera viñeta en la cita anterior era:

- el valor representable más negativo o cero para un tipo entero sin signo, si el campo representa un valor negativo demasiado grande para ser representado en val . ios_base::failbit se asigna a err .

2 std::strtoull devuelve ULLONG_MAX porque (gracias @NathanOliver) - C / 7.22.1.4.5:

Si la secuencia del sujeto tiene la forma esperada y el valor de base es cero, la secuencia de caracteres que comienza con el primer dígito se interpreta como una constante entera de acuerdo con las reglas de 6.4.4.1. [...] Si la secuencia del sujeto comienza con un signo menos, el valor resultante de la conversión se anula (en el tipo de retorno).

Por ejemplo,

#include <iostream> int main() { unsigned n{}; std::cin >> n; std::cout << n << '' '' << (bool)std::cin << std::endl; }

Cuando se ingresa -1 , clang 6.0.0 genera 0 0 mientras que gcc 7.2.0 produce 4294967295 1 . Me pregunto quién tiene razón. O tal vez ambos son correctos para que el estándar no especifique esto? Por fallar, entiendo que (bool)std::cin se evalúa como falso. clang 6.0.0 falla entrada -0 también.


La semántica prevista de su comando std::cin >> n se describe here (como, aparentemente, se llama a std::num_get::get() para esta operación). Ha habido algunos cambios semánticos en esta función, específicamente en la elección de colocar 0 o no, en C ++ 11 y luego nuevamente en C ++ 17.

No estoy del todo seguro, pero creo que estas diferencias pueden explicar el comportamiento diferente que está viendo.