Ich bin selbst davon überzeugt, dass ich in einem Projekt, an dem ich arbeite, in den meisten Fällen die beste Wahl getroffen habe, obwohl der darin enthaltene Wert niemals negativ sein kann. (Einfachere Umkehrung für Schleifen, weniger Möglichkeit für Fehler usw., insbesondere für Ganzzahlen, die nur Werte zwischen 0 und, sagen wir, 20 enthalten können.)
Die Mehrzahl der Stellen, an denen das schief geht, ist eine einfache Iteration eines std :: vectors, oft ein Array in der Vergangenheit und später ein std :: vector. Diese Schleifen sehen also generell so aus:
%Vor%Da dieses Muster so häufig verwendet wird, neigt die Menge an Compiler-Warn-Spam zu diesem Vergleich zwischen signierten und unsignierten Typen dazu, nützlichere Warnungen auszublenden. Beachten Sie, dass wir definitiv keine Vektoren mit mehr als INT_MAX Elementen haben, und beachten Sie, dass wir bis jetzt zwei Möglichkeiten benutzt haben, die Compiler-Warnung zu beheben:
%Vor%Dies funktioniert in der Regel, aber es kann stumm werden, wenn die Schleife einen Code wie 'if (i-1 & gt; = 0) ...' usw. enthält.
%Vor%Diese Änderung hat keine Nebenwirkungen, aber sie macht die Schleife viel weniger lesbar. (Und es ist mehr Tipparbeit.)
Also kam ich auf die folgende Idee:
%Vor%Was Sie sehen, sind im Grunde die STL-Klassen mit den Methoden, die size_type zurückgeben, um nur 'int' zurückzugeben. Die Konstruktoren werden benötigt, da diese nicht vererbt werden.
Was würden Sie davon als Entwickler halten, wenn Sie eine solche Lösung in einer vorhandenen Codebasis sehen würden?
Würdest du denken, dass sie die STL neu definieren, was für ein riesiger WTF !, oder würdest du denken, dass dies eine nette einfache Lösung ist, um Bugs zu verhindern und die Lesbarkeit zu erhöhen? Oder vielleicht würden Sie lieber sehen, dass wir (einen halben) Tag oder so damit verbracht haben, alle diese Schleifen zu ändern, um std :: vector & lt; & gt; :: iterator?
zu verwenden(Insbesondere wenn diese Lösung kombiniert wurde mit dem Verbot der Verwendung von vorzeichenlosen Typen für alles außer Rohdaten (z. B. unsigned char) und Bitmasken).
Ja, ich stimme Richard zu. Sie sollten niemals 'int'
als Zählvariable in einer Schleife wie diesen verwenden. Das Folgende ist, wie Sie verschiedene Schleifen mit Hilfe von Indizes machen möchten (obwohl es wenig Grund gibt, dies gelegentlich nützlich zu sein).
Sie können dies tun, was perfekt definiert ist:
%Vor%Bald, mit C ++ 1x (nächste C ++ Version) kommst du gut, du kannst es so machen:
%Vor%Das Dekrementieren unter 0 führt dazu, dass i umhergeht, weil es nicht signiert ist.
Das sollte niemals ein Argument sein, um es falsch zu machen (mit 'int'
).
Der C ++ Standard definiert in 23.1 p5 Container Requirements
, das T::size_type
, für T
ist einige Container
, dass dieser Typ eine implementation definierte vorzeichenlose Ganzzahl ist. Wenn Sie nun std::size_t
für i
oben verwenden, können sich die Fehler im Hintergrund abspielen. Wenn T::size_type
kleiner oder größer als std::size_t
ist, wird% ce_de% überlaufen oder nicht bis i
wenn (std::size_t)-1
. Ebenso wäre der Zustand der Schleife komplett gebrochen.
Nicht öffentlich aus STL-Containern ableiten. Sie haben nichtvirtuelle Destruktoren, die undefiniertes Verhalten aufrufen, wenn jemand eines Ihrer Objekte durch einen Zeiger auf die Basis löscht. Wenn Sie z.B. Von einem Vektor aus, machen Sie es privat und legen Sie die Teile offen, die Sie mit using
declarations freilegen müssen.
Hier würde ich nur eine size_t
als Schleifenvariable verwenden. Es ist einfach und lesbar. Das Plakat, das bemerkt hat, dass die Verwendung eines int
-Index dich als n00b exponiert, ist korrekt. Wenn Sie jedoch einen Iterator verwenden, um einen Vektor zu durchlaufen, werden Sie als etwas erfahrener n00b exponiert - einer, der nicht erkennt, dass der tiefgestellte Operator für Vektor eine konstante Zeit ist. ( vector<T>::size_type
ist korrekt, aber unnötig ausführlich IMO).
Obwohl ich nicht denke, "Iteratoren verwenden, sonst siehst du n00b aus" ist eine gute Lösung für das Problem, abgeleitet von std :: vector erscheint viel schlimmer als das.
Zuerst erwarten Entwickler, dass Vektor std: .vector ist und map std :: map lautet. Zweitens skaliert Ihre Lösung nicht für andere Container oder andere Klassen / Bibliotheken, die mit Containern interagieren.
Ja, Iteratoren sind hässlich, Iterator-Schleifen sind nicht gut lesbar und typedefs verdecken nur das Durcheinander. Aber zumindest skalieren sie, und sie sind die kanonische Lösung.
Meine Lösung? ein stl-for-each-Makro. Das ist nicht ohne Probleme (hauptsächlich, es ist ein Makro, yuck), aber es geht über die Bedeutung. Es ist nicht so fortgeschritten wie z.B. dieses , aber macht den Job.
Verwenden Sie einen Iterator. Bald können Sie den Typ "auto" verwenden, um eine bessere Lesbarkeit (eines Ihrer Anliegen) zu erhalten:
%Vor%Der einfachste Ansatz besteht darin, das Problem zu umgehen, indem Sie Iteratoren, bereichsbasierte for-Schleifen oder Algorithmen verwenden:
%Vor%Dies ist eine nette Lösung, wenn Sie den Indexwert nicht wirklich benötigen. Es behandelt auch leicht umgekehrte Schleifen.
Ein anderer Ansatz besteht darin, den Größen-Typ des Containers zu verwenden.
%Vor% Sie können auch std::size_t
(von & lt; cstddef & gt;) verwenden. Es gibt diejenigen, die (richtig) darauf hinweisen, dass std::size_t
möglicherweise nicht der gleiche Typ wie std::vector<T>::size_type
ist (obwohl es normalerweise ist). Sie können jedoch sicher sein, dass der size_type
des Containers in std::size_t
passt. Alles ist in Ordnung, es sei denn, Sie verwenden bestimmte Stile für Reverse-Loops. Mein bevorzugter Stil für eine umgekehrte Schleife ist dies:
Mit diesem Stil können Sie sicher std::size_t
verwenden, auch wenn es sich um einen größeren Typ als std::vector<T>::size_type
handelt. Die Art der umgekehrten Schleifen, die in einigen der anderen Antworten gezeigt wird, erfordert das Umwandeln von -1 in genau den richtigen Typ und kann daher nicht den einfacheren Typ std::size_t
verwenden.
Wenn Sie wirklich einen signierten Typ verwenden möchten (oder Ihren Style Guide fordert praktisch eine ), wie int
, dann können Sie diese kleine Funktionsvorlage verwenden, die die zugrundeliegende Annahme in Debug-Builds überprüft und die Konvertierung explizit macht, so dass Sie die Compiler-Warnmeldung nicht erhalten:
Jetzt können Sie schreiben:
%Vor%Oder kehren Sie Schleifen auf traditionelle Weise um:
%Vor% Der size_as_int
Trick ist nur etwas mehr Tipping als die Schleifen mit den impliziten Konvertierungen, Sie erhalten die zugrunde liegende Annahme zur Laufzeit überprüft, Sie stummschalten die Compiler-Warnung mit der expliziten Umwandlung, Sie erhalten die gleiche Geschwindigkeit wie Nicht-Debug-Builds weil es fast sicher inline ist, und der optimierte Objektcode sollte nicht größer sein, da die Vorlage nichts tut, was der Compiler nicht bereits implizit getan hat.
Sie überdenken das Problem.
Die Verwendung einer size_t-Variablen ist vorzuziehen, aber wenn Sie Ihren Programmierern nicht vertrauen, unsigned korrekt zu verwenden, gehen Sie mit der Besetzung und befassen Sie sich nur mit der Hässlichkeit. Holen Sie sich einen Praktikanten, um sie alle zu ändern, und machen Sie sich danach keine Sorgen. Schalte Warnungen als Fehler ein und es werden keine neuen eingeschlichen. Deine Schleifen sind jetzt vielleicht "hässlich", aber das kannst du als Folgen deiner religiösen Haltung auf unterzeichnet und nicht signiert verstehen.
Tags und Links c++ stl coding-style unsigned