web-dev-qa-db-fra.com

La lecture de négatif en unsigned doit-elle échouer via std :: cin (gcc, clang en désaccord)?

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.

22
Lingxi

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

18
Holt

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.

0
einpoklum