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::failbitse 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::failbitse 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.