Warum wird std :: ssize () in C ++ 20 eingeführt?

99

C ++ 20 führte die std::ssize()freie Funktion wie folgt ein:

template <class C>
    constexpr auto ssize(const C& c)
        -> std::common_type_t<std::ptrdiff_t,
                              std::make_signed_t<decltype(c.size())>>;

Eine mögliche Implementierung scheint zu verwenden static_cast, um den Rückgabewert der size()Mitgliedsfunktion von cl ass C in sein vorzeichenbehaftetes Gegenstück umzuwandeln .

Da die size()Mitgliedsfunktion von C immer nicht negative Werte zurückgibt, warum sollte jemand sie in vorzeichenbehafteten Variablen speichern wollen? Wenn man es wirklich will, ist es eine Frage der Einfachheit static_cast.

Warum wird std::ssize()in C ++ 20 eingeführt?

John Z. Li
quelle
4
@ Jarod42 Ist die Implementierung nicht definiert anstatt undefiniert? (signierter Überlauf ist undefiniert, aber signierte Konvertierung ist implementierungsdefiniert)
Phön
8
Wenn sie nur auch ssizeofOperator hinzufügen .
Geza
3
Dies könnte etwas verwandt sein: stackoverflow.com/questions/30395205/…
Marco13
10
@ JohnZ.Li Auf die Gefahr hin, zu unkonstruktiv zu klingen: Ich denke, dass das gesamte Typsystem von C ++ in Bezug auf die Integer-Typen kaputt ist. Sicher, man kann argumentieren, dass einige Macken (wie nicht zu wissen, wie viele Bits a charhat) von C geerbt und zumindest etwas gemildert werden (u)intX_t, aber es ist immer noch eine endlose Quelle von ebenso subtilen und kritischen Fehlern. Dinge wie ssizesind nur Patches, und es wird eine Weile dauern (vielleicht "für immer"), bis dies in die gängigen "Best Practices Guides" einfließt, denen die Leute rigoros folgen (können).
Marco13
6
@ Marco13: Auf der anderen Seite, die C / C ++ Typsystem (wie zum Beispiel Java festen Typen System gegen), abgesehen von C / C ++ Code zu arbeiten auf Architekturen , bei denen den meist anderen Sprachen quaken, von ermöglicht nicht erlaubt zuständige Lehrer einige wichtige zu bekommen Unterricht in den Kopf eines Schülers. Nicht die ganze Welt ist 64bit. Und nein, nicht die ganze Welt verwendet 8-Bit-Zeichen. Es ist kinderleicht , mit diesen Dingen umzugehen, und es macht Sie zu einem besseren Entwickler, wenn nur Ausbilder dies von Anfang an lehren würden . (Und nur um sicherzugehen, dass Sie wissen , dass die (u)intX_tTypen optional sind , oder?)
DevSolar

Antworten:

69

Die Begründung wird in diesem Papier beschrieben . Ein Zitat:

Bei der Übernahme von span in C ++ 17 wurde eine vorzeichenbehaftete Ganzzahl sowohl als Index als auch als Größe verwendet. Teilweise sollte dies die Verwendung von "-1" als Sentinel-Wert ermöglichen, um einen Typ anzugeben, dessen Größe zum Zeitpunkt der Kompilierung nicht bekannt war. Da jedoch ein STL-Container, dessen size () -Funktion einen vorzeichenbehafteten Wert zurückgab, problematisch war, wurde P1089 eingeführt, um das Problem zu "beheben". Es erhielt mehrheitliche Unterstützung, jedoch nicht die für den Konsens erforderliche 2-zu-1-Marge.

Dieses Dokument, P1227, war ein Vorschlag zum Hinzufügen von std :: ssize- und member ssize () -Funktionen, die keine Mitglieder sind. Die Einbeziehung dieser würde bestimmten Code viel einfacher machen und die Vermeidung unerwünschter Vorzeichen bei Größenberechnungen ermöglichen. Die Idee war, dass der Widerstand gegen P1089 abnehmen würde, wenn ssize () für alle Container verfügbar gemacht würde, sowohl über std :: ssize () als auch als Elementfunktionen.

Nadav Har'El
quelle
30
Das for(int i = 0; i < container.ssize() - 1; ++i)Beispiel ist auch ziemlich überzeugend
Caleth
7
@ John scheint mir in der Tat, dass sie das gleiche wie string :: npos tun und einfach size_t (-1) als speziellen Wert verwenden könnten.
Rubenvb
15
@ JohnZ.Li Es wird lange als Fehler angesehen, dass STL-Größentypen ohne Vorzeichen sind. Jetzt ist es leider zu spät, um es zu reformieren. Die Bereitstellung einer kostenlosen Funktion ist das Beste, was wir derzeit tun können.
LF
16
@LF: Es war Herb Sutter in einer Konferenz (vielleicht hat Bjarne das auch gesagt). Aber er ist ein bisschen falsch. Bei 32-Bit / 64-Bit-Computern wäre die signierte Größe besser (also hat er Recht). Aber früher (16-Bit-Größen) wäre die signierte Größe schlecht gewesen (zum Beispiel hätten wir nur 32-KByte-Arrays zuweisen können).
Geza
11
@LF: Ich habe festgestellt, dass Herb dies erwähnt: youtube.com/watch?v=Puio5dly9N8&t=2667 . Wenn er sagt, dass "in der Praxis nicht viel auftaucht", ist das heutzutage wahr. Aber vor> 20 Jahren (16-Bit-Systeme) stimmte das überhaupt nicht. Es war also kein großer Fehler, bei der Entwicklung der STL unsigned zu verwenden.
Geza
50

Unentgeltlich von Eric Niebler gestohlen :

'Unsigned types signal that a negative index/size is not sane'war die vorherrschende Weisheit, als die STL zum ersten Mal entworfen wurde. Aber logischerweise muss eine Anzahl von Dingen nicht positiv sein. Möglicherweise möchte ich eine Zählung in einer vorzeichenbehafteten Ganzzahl beibehalten, um die Anzahl der Elemente anzugeben, die einer Sammlung hinzugefügt oder daraus entfernt wurden. Dann möchte ich das mit der Größe der Sammlung kombinieren. Wenn die Größe der Sammlung nicht signiert ist, muss ich jetzt signierte und nicht signierte Arithmetik mischen, was eine Fehlerfarm ist. Compiler warnen davor, aber da das Design der STL Programmierer so ziemlich in diese Situation zwingt, ist die Warnung so häufig, dass die meisten Leute sie ausschalten. Das ist eine Schande, denn dies verbirgt echte Fehler.

Die Verwendung von Ints ohne Vorzeichen in Schnittstellen ist nicht der Segen, den viele Leute denken. Wenn ein Benutzer versehentlich eine leicht negative Zahl an die API übergibt, wird diese plötzlich zu einer riesigen positiven Zahl. Wenn die API die Nummer als signiert angenommen hat, kann sie die Situation erkennen, indem sie behauptet, dass die Nummer größer oder gleich Null ist.

Wenn wir die Verwendung von nicht signierten Ints auf Bit Twiddling (z. B. Masken) beschränken und überall signierte Ints verwenden, ist es weniger wahrscheinlich, dass Fehler auftreten, und es ist einfacher, sie zu erkennen, wenn sie auftreten.

sp2danny
quelle
6
Swift verfolgt diesen Ansatz, obwohl es nicht darum geht, dass negativ signierte Zahlen als massive vorzeichenlose Zahlen neu interpretiert werden (da es keine impliziten Casts gibt, die Sie wirklich in dieses verrückte Spaßhaus bringen). Sie gehen einfach so vor, dass (Maschinenwortgröße) Intdie gängigen Währungstypen von ganzen Zahlen sein sollten, auch wenn nur positive Zahlen sinnvoll sind (z. B. Indizieren eines Arrays). Jede Abweichung davon sollte begründet sein. Es ist schön, sich nicht überall um Casts sorgen zu müssen.
Alexander - Reinstate Monica
3
@ JohnZ.Li In der Tat, "unsigned int als schädlich für Java"
Nayuki