Frage

ich bin überzeugt, dass in einem Projekt auf unterzeichnet ganzen Zahlen sind die beste Wahl in den meisten Fällen arbeite ich, obwohl der enthaltene Wert innerhalb niemals negativ sein. (Simpler Rückseite für Schleifen, weniger Chancen für Fehler, usw., insbesondere für ganze Zahlen, die nur Werte zwischen 0 halten kann und, sagen wir, 20, sowieso.)

Die meisten der Orte, wo dies schief geht ist eine einfache Iteration eines std :: vector, häufig verwendet, um dieses Feld in der Vergangenheit zu sein und hat sich später zu einem std :: vector geändert. Also diese Schleifen sehen in der Regel wie folgt aus:

for (int i = 0; i < someVector.size(); ++i) { /* do stuff */ }

Da dieses Muster so oft verwendet wird, neigt die Menge des Compiler-Warnung Spam diesen Vergleich zwischen mit und ohne Vorzeichen Typen nützliche Warnungen zu verbergen. Beachten Sie, dass wir auf jeden Fall nicht über Vektoren mit mehr als INT_MAX Elemente, und beachten Sie, dass wir bis jetzt zwei Arten verwendet Compiler-Warnung zu beheben:

for (unsigned i = 0; i < someVector.size(); ++i) { /*do stuff*/ }

Dies funktioniert in der Regel aber vielleicht leise brechen, wenn die Schleife einen Code enthält wie 'if (i-1> = 0) ...', etc.

for (int i = 0; i < static_cast<int>(someVector.size()); ++i) { /*do stuff*/ }

Diese Änderung hat keine Nebenwirkungen, aber es macht die Schleife viel weniger lesbar machen. (Und es ist mehr tippen.)

So kam ich auf die folgende Idee:

template <typename T> struct vector : public std::vector<T>
{
    typedef std::vector<T> base;

    int size() const     { return base::size(); }
    int max_size() const { return base::max_size(); }
    int capacity() const { return base::capacity(); }

    vector()                  : base() {}
    vector(int n)             : base(n) {}
    vector(int n, const T& t) : base(n, t) {}
    vector(const base& other) : base(other) {}
};

template <typename Key, typename Data> struct map : public std::map<Key, Data>
{
    typedef std::map<Key, Data> base;
    typedef typename base::key_compare key_compare;

    int size() const     { return base::size(); }
    int max_size() const { return base::max_size(); }

    int erase(const Key& k) { return base::erase(k); }
    int count(const Key& k) { return base::count(k); }

    map() : base() {}
    map(const key_compare& comp) : base(comp) {}
    template <class InputIterator> map(InputIterator f, InputIterator l) : base(f, l) {}
    template <class InputIterator> map(InputIterator f, InputIterator l, const key_compare& comp) : base(f, l, comp) {}
    map(const base& other) : base(other) {}
};

// TODO: similar code for other container types

Was Sie sehen, ist im Grunde die STL-Klassen mit den Methoden, die size_type überschriebene Rückkehr nur ‚int‘ zurückzukehren. Die Konstrukteure sind erforderlich, da diese nicht vererbt.

Was würden Sie davon halten, als Entwickler, wenn Sie eine Lösung wie diese in einer bestehenden Code-Basis sehen würden?

Würden Sie denken, ‚whaa, sie sind die STL neu zu definieren, was für eine riesige WTF!‘, Oder würden Sie denken, dieses eine nette einfache Lösung ist, um Fehler zu vermeiden und die Lesbarkeit zu erhöhen. Oder vielleicht möchten Sie lieber sehen wir hatten ausgegeben (die Hälfte) pro Tag oder so weiter, all diese Schleifen zu ändern std verwenden :: vector <> :: iterator?

(Insbesondere, wenn diese Lösung wurde aber Rohdaten mit Verbot der Verwendung von unsigned Typen für alles kombiniert (zum Beispiel unsigned char) und Bit-Masken.)

War es hilfreich?

Lösung

ich diese Community Wiki gemacht ... Bitte bearbeiten. Ich habe nicht mit dem Rat gegen „int“ zustimmen mehr. Ich sehe es jetzt als nicht schlecht.

Ja, ich stimme mit Richard. Sie sollten sich nicht als Zählvariable in einer Schleife wie die benutzen 'int'. Hier finden Sie, wie Sie könnten verschiedene Loops Indizes tun wollen (althought es wenig Grund, gelegentlich kann dies nützlich sein).

Weiterleiten

for(std::vector<int>::size_type i = 0; i < someVector.size(); i++) {
    /* ... */
}

Rückwärts

Sie können dies tun, die behaivor perfekt definiert ist:

for(std::vector<int>::size_type i = someVector.size() - 1; 
    i != (std::vector<int>::size_type) -1; i--) {
    /* ... */
}

Bald mit c ++ 1x (neben C ++ Version) gut voran kommen, können Sie es wie folgt tun:

for(auto i = someVector.size() - 1; i != (decltype(i)) -1; i--) {
    /* ... */
}

Dekrementoperatoren unter 0 bewirkt, dass ich um wickeln, weil es nicht signiert ist.

Aber ohne Vorzeichen werden Fehler in

schlürfen machen

Das sollte nie ein Argument sein, um es der falsche Weg (mit 'int') zu machen.

Warum std :: size_t oben nicht verwenden?

Die C ++ Norm definiert in 23.1 p5 Container Requirements, dass T::size_type, für T einige Container zu sein, dass diese Art einige Implementierung ist unsigned Integraltyp definiert. Nun, oben mit std::size_t für i lassen Fehler in leise schlürfen. Wenn T::size_type kleiner oder größer als std::size_t ist, dann wird es überlaufen i, oder auch nicht, wenn (std::size_t)-1 zu someVector.size() == 0 aufzustehen. Ebenso würde der Zustand der Schleife vollständig gebrochen worden ist.

Andere Tipps

Sie leiten nicht öffentlich von STL-Containern. Sie haben nicht virtuelle Destruktoren, die nicht definiertes Verhalten aufruft, wenn jemand eine Ihrer Objekte durch einen Zeiger auf Basis löscht. Wenn Sie müssen ableiten z.B. von einem Vektor es privat tun und die Teile, die Sie mit using Erklärungen aussetzen müssen aussetzen.

Hier würde ich nur eine size_t als Schleifenvariable verwenden. Es ist ganz einfach und lesbar. Das Plakat, das bemerkte, dass ein int Index mit aussetzt Sie als n00b korrekt ist. Allerdings setzen Sie mit einem Iterator Schleife über einen Vektor als etwas erfahreneren n00b - derjenige, der nicht erkennen, dass der Index-Operator für den Vektor konstante Zeit ist. (vector<T>::size_type ist genau, aber unnötig ausführliche IMO).

Während ich glaube nicht „Iteratoren verwenden, sonst Sie n00b suchen“ ist eine gute Lösung für das Problem, von std :: vector Ableitung erscheint viel schlimmer als das.

Als erster Entwickler erwartet Vektor std werden: .Vector und map STD wird :: map. Zweitens sieht Ihre Lösung nicht für andere Behältnisse skalieren, oder für andere Klassen / Bibliotheken, die mit Containern in Wechselwirkung treten.

Ja, Iteratoren hässlich sind, Iterator Schleifen sind nicht sehr gut lesbar, und typedefs nur das Chaos verschleiern. Aber zumindest, sie tun Maßstab, und sie sind die kanonische Lösung.

Meine Lösung? eine stl-for-each Makro. Das ist nicht ohne Probleme (vor allem, es ist ein Makro, igitt), aber es wird über die Bedeutung. Es ist nicht so wie zum Beispiel vorgeschoben diese , sondern macht den Job.

verwenden Auf jeden Fall einen Iterator. Bald werden Sie die ‚auto‘ Typ verwenden können, zur besseren Lesbarkeit (eine Ihrer Sorgen) wie folgt aus:

for (auto i = someVector.begin();
     i != someVector.end();
     ++i)

überspringen Index

Der einfachste Ansatz ist das Problem mit Iteratoren zu umgehen, bereichsbasierte for-Schleifen oder Algorithmen:

for (auto it = begin(v); it != end(v); ++it) { ... }
for (const auto &x : v) { ... }
std::for_each(v.begin(), v.end(), ...);

Dies ist eine schöne Lösung, wenn Sie tatsächlich den Indexwert nicht brauchen. Es behandelt auch Umkehrschleifen leicht.

Verwenden Sie einen geeigneten Typ ohne Vorzeichen

Ein weiterer Ansatz ist die Behältergröße Typen zu verwenden.

for (std::vector<T>::size_type i = 0; i < v.size(); ++i) { ... }

Sie können auch std::size_t verwenden (von ). Es gibt diejenigen, die (richtig), dass std::size_t können darauf hinweisen, nicht vom gleichen Typ wie std::vector<T>::size_type sein (obwohl es in der Regel ist). Sie können jedoch sicher sein, dass der Behälter size_type in einem std::size_t passen. Also alles ist in Ordnung, wenn man für die Umkehrschleifen bestimmte Stile verwenden. Mein bevorzugter Stil für eine Rückwärtsschleife ist dies:

for (std::size_t i = v.size(); i-- > 0; ) { ... }

Mit diesem Stil können Sie sicher std::size_t verwenden, auch wenn es eine größere Art als std::vector<T>::size_type. Der Stil der Umkehrschleifen in einige der anderen Antworten gezeigt erfordert Gießen eine -1 bis genau die richtige Art und somit nicht den einfacher zu Typ std::size_t verwenden kann.

Verwenden Sie einen signierten Typen (vorsichtig!)

Wenn Sie wirklich wollen einen signierten Typen verwenden (oder wenn IhrFormal Style Guide erfordert praktisch eine ), wie int, dann können Sie diese winzige Funktionsvorlage verwenden, die die zugrunde liegende Annahme überprüft in Debug-Builds und macht die Umwandlung explizit so, dass Sie nicht die Compiler Warnmeldung erhielten:

#include <cassert>
#include <cstddef>
#include <limits>

template <typename ContainerType>
constexpr int size_as_int(const ContainerType &c) {
    const auto size = c.size();  // if no auto, use `typename ContainerType::size_type`
    assert(size <= static_cast<std::size_t>(std::numeric_limits<int>::max()));
    return static_cast<int>(size);
}

Jetzt können Sie schreiben:

for (int i = 0; i < size_as_int(v); ++i) { ... }

oder Umkehrschleifen in der traditionellen Art und Weise:

for (int i = size_as_int(v) - 1; i >= 0; --i) { ... }

Der size_as_int Trick ist nur etwas mehr Typisierung als die Schleifen mit den impliziten Konvertierungen, können Sie die zugrunde liegende Annahme zur Laufzeit überprüft erhalten, zum Schweigen bringen Sie den Compiler mit den expliziten Cast Warnung, erhalten Sie die gleiche Geschwindigkeit wie nicht-debug, weil es baut mit ziemlicher Sicherheit inlined werden, und der Objektcode optimiert sollte nicht größer sein, weil die Vorlage nichts tut der Compiler nicht bereits implizit tun.

Sie sind Grübeln das Problem.

eine size_t Variable ist vorzuziehen, aber wenn Sie nicht vertrauen Ihre Programmierer korrekt ohne Vorzeichen zu verwenden, mit der Besetzung gehen und nur mit der Hässlichkeit beschäftigen. ein Praktikum bekommen sie alle und keine Sorge darüber nach, das zu ändern. Schalten Sie Warnungen als Fehler und keine neuen werden einschleichen. Ihre Schleifen sein kann „hässlich“ jetzt, aber Sie wissen, dass, wenn die Folgen Ihrer religiösen Haltung zum Vergleich ohne Vorzeichen.

vector.size() gibt eine size_t var, so dass nur int ändern size_t und es sollte in Ordnung sein.

Richard Antwort ist richtig, mit der Ausnahme, dass es eine Menge Arbeit für eine einfache Schleife ist.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top