wake-up-neo.com

Soll Negatives in unsigned fail über std :: cin gelesen werden (gcc, clang anderer Meinung)

Zum Beispiel,

#include <iostream>

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

Wenn -1 eingegeben wird, gibt clang 6.0.00 0 aus, während gcc 7.2.04294967295 1 ausgibt. Ich frage mich, wer richtig ist. Oder vielleicht sind beide für den Standard korrekt, spezifiziert dies nicht? Im Fehlerfall nehme ich an, dass (bool)std::cin als falsch bewertet wird. clang 6.0.0 schlägt auch die Eingabe -0 fehl.

22
Lingxi

Ich denke, dass beide in C++ 17 falsch sind1 und dass die erwartete Ausgabe sein sollte:

4294967295 0

Dies ist jedoch schwer vom Standard zu verstehen ...

Der Standard sagt - [facet.num.get.virtuals # 3.3] :

Die in Stufe 2 (das Feld) angesammelte Folge von Zeichen wird durch die Regeln einer der im Header deklarierten Funktionen in einen numerischen Wert umgewandelt. <cstdlib>:

  • Für einen vorzeichenbehafteten ganzzahligen Wert die Funktion strtoll.

  • Für einen vorzeichenlosen Integerwert ist die Funktion strtoull.

  • Für einen Gleitkommawert ist die Funktion strtold.

Wir greifen also auf std::strtoull zurück, das zurückkehren muss2 ULLONG_MAX und nicht errno gesetzt (was beide Compiler tun).

Aber im selben Block (Hervorhebung gehört mir):

Der zu speichernde numerische Wert kann einer der folgenden sein:

  • null, wenn die Konvertierungsfunktion nicht das gesamte Feld konvertiert.

  • der positivste (oder negativste) darstellbare Wert, wenn das -Feld, das in einen vorzeichenbehafteten Ganzzahlentyp konvertiert werden soll, einen Wert darstellt, der zu groß ist, um positiv (oder negativ) in val dargestellt zu werden.

  • der positivste darstellbare Wert, wenn das -Feld, das in einen vorzeichenlosen Integer-Typ umgewandelt werden soll, einen Wert darstellt, der nicht in val dargestellt werden kann.

  • ansonsten der umgerechnete Wert.

Der resultierende numerische Wert wird in val gespeichert. Wenn die Konvertierungsfunktion nicht das gesamte Feld konvertiert oder wenn das Feld einen Wert außerhalb des Bereichs der darstellbaren Werte darstellt, wird ios_­base​::​failbiterr zugewiesen.

Beachten Sie, dass alle diese Informationen über "zu konvertierendes Feld" und nicht über den von std::strtoull zurückgegebenen Wert sprechen. Das Feld hier ist eigentlich die verbreiterte Folge des Zeichens '-', '1'.

Da das Feld einen Wert (-1) darstellt, der nicht durch eine unsigned dargestellt werden kann, sollte der zurückgegebene Wert UINT_MAX sein und das Failbit sollte auf std::cin gesetzt sein.


1clang war eigentlich vor C++ 17 richtig, da der dritte Punkt im obigen Zitat:

- der negativste darstellbare Wert oder Null für einen vorzeichenlosen Integer-Typ, wenn das Feld einen Wert darstellt, der zu groß ist, um in val dargestellt zu werden. ios_base::failbit ist err zugewiesen.

2 std::strtoull gibt ULLONG_MAX zurück, weil (danke @NathanOliver) - C/7.22.1.4.5:

Wenn die Subjektsequenz die erwartete Form hat und der Wert von base Null ist, wird die Zeichenfolge, die mit der ersten Ziffer beginnt, als Ganzzahlkonstante gemäß den Regeln von 6.4.4.1 ..__ interpretiert. [...] Wenn die Betreffsequenz mit einem Minuszeichen beginnt, wird der aus der Konvertierung resultierende Wert negiert (im Rückgabetyp).

18
Holt

Die beabsichtigte Semantik Ihres std::cin >> n-Befehls wird beschrieben hier (da anscheinend std::num_get::get() für diese Operation aufgerufen wird). In dieser Funktion wurden einige Änderungen der Semantik vorgenommen, insbesondere w.r.t. die Wahl, ob 0 oder nicht platziert werden soll, in C++ 11 und dann erneut in C++ 17.

Ich bin nicht ganz sicher, aber ich glaube, dass diese Unterschiede für das unterschiedliche Verhalten verantwortlich sind, das Sie sehen.

0
einpoklum