Par exemple,
#include <iostream>
int main() {
unsigned n{};
std::cin >> n;
std::cout << n << ' ' << (bool)std::cin << std::endl;
}
Lorsque vous saisissez -1
, version 6.0.0 affiche 0 0
tandis que gcc 7.2.0 affiche 4294967295 1
. Je me demande qui a raison. Ou peut-être que les deux sont corrects pour la norme ne spécifie pas cela? Par défaut, je considère que (bool)std::cin
est évalué comme faux. Clang 6.0.0 échoue aussi l'entrée -0
.
Je pense que les deux ont tort en C++ 171 et que le résultat attendu devrait être:
4294967295 0
Ceci est cependant difficile à saisir de la norme ...
La norme indique - [facet.num.get.virtuals # 3.3] :
La séquence de caractères accumulés à l'étape 2 (le champ) est convertie en une valeur numérique par les règles d'une des fonctions déclarées dans l'en-tête
<cstdlib>
:
Pour une valeur entière signée, la fonction
strtoll
.Pour une valeur entière non signée, la fonction
strtoull
.Pour une valeur à virgule flottante, la fonction
strtold
.
Nous retombons donc dans std::strtoull
, qui doit retourner2 ULLONG_MAX
et non défini errno
dans ce cas (ce que font les deux compilateurs).
Mais dans le même bloc (emphasis est à moi):
La valeur numérique à stocker peut être l'une des suivantes:
zéro, si la fonction de conversion ne convertit pas le champ entier.
la valeur représentable la plus positive (ou négative), si le champ à convertir en un type entier signé représente une valeur trop grande positive (ou négative) pour être représentée dans
val
.la valeur représentable la plus positive, si le champ à convertir en un type entier non signé représente une valeur qui ne peut pas être représentée dans
val
.la valeur convertie, sinon.
La valeur numérique résultante est stockée dans
val
. Si la fonction de conversion ne convertit pas l'intégralité du champ ou si le champ représente une valeur en dehors de la plage de valeurs pouvant être représentées,ios_base::failbit
est attribué àerr
.
Notez que toutes ces discussions sur le "champ à convertir" et non la valeur réelle renvoyée par std::strtoull
. Le champ ici est en fait la séquence élargie du caractère '-', '1'
.
Étant donné que le champ représente une valeur (-1) qui ne peut pas être représentée par un unsigned
, la valeur renvoyée doit être UINT_MAX
et le code de défaillance doit être défini sur std::cin
.
1clang
était en fait juste avant C++ 17 car la troisième puce de la citation ci-dessus était:
- la valeur représentable la plus négative ou zéro pour un type entier non signé, si le champ représente une valeur trop grande négative pour être représentée dans
val
.ios_base::failbit
est attribué àerr
.
2 std::strtoull
retourne ULLONG_MAX
car (merci @NathanOliver) - C/7.22.1.4.5:
Si la séquence sujet a la forme attendue et que la valeur de base est égale à zéro, la séquence de caractères commençant par le premier chiffre est interprétée comme une constante entière conformément aux règles de 6.4.4.1 . [...] Si la séquence de sujet commence par un signe moins, la valeur résultant de la conversion est annulée (dans le type de retour).
La sémantique prévue pour votre commande std::cin >> n
est décrite ici (comme, apparemment, std::num_get::get()
est appelé pour cette opération). Il y a eu quelques changements de sémantique dans cette fonction, notamment w.r.t. le choix de placer ou non 0, en C++ 11, puis à nouveau en C++ 17.
Je ne suis pas tout à fait sûr, mais je pense que ces différences peuvent expliquer le comportement différent que vous observez.