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 aerr
.
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 aerr
.
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.