wake-up-neo.com

Vorbereitung für std :: iterator wird veraltet

Am 21. Märzst Das Normungsgremium stimmte der Ablehnung von std::iterator zu, die in P0174 vorgeschlagen wurde:

Die lange Abfolge von ungültigen Argumenten ist für den Leser weitaus weniger klar, als lediglich die erwarteten typedefs in der Klassendefinition selbst anzugeben. Dies ist der Ansatz des aktuellen Arbeitsentwurfs, der dem in festgelegten Muster folgt. c ++ 14

Vor c ++ 17 wurde die Vererbung von std::iterator Aufgefordert, das Langeweile bei der Implementierung von Iterator Boilerplate zu beseitigen. Für die Abschreibung ist jedoch eines der folgenden Dinge erforderlich:

  1. Ein Iterator-Boilerplate muss nun alle erforderlichen typedefs enthalten
  2. Algorithmen, die mit Iteratoren arbeiten, müssen nun auto verwenden, anstatt vom Iterator abhängig zu sein, um Typen zu deklarieren
  3. Loki Astari hat vorgeschlagen dass std::iterator_traits aktualisiert werden kann, ohne von std::iterator Zu erben

Kann mir jemand erklären, welche dieser Optionen ich erwarten sollte, wenn ich benutzerdefinierte Iteratoren mit Blick auf c ++ 17 Kompatibilität entwerfe?

57
Jonathan Mee

Die besprochenen Alternativen sind klar, aber ich bin der Meinung, dass ein Codebeispiel benötigt wird.

Da es keinen Sprachersatz gibt und Sie sich nicht auf boost oder Ihre eigene Version der Iterator-Basisklasse verlassen müssen, wird der folgende Code, der std::iterator Verwendet, im Code darunter fixiert.

Mit std::iterator

template<long FROM, long TO>
class Range {
public:
    // member typedefs provided through inheriting from std::iterator
    class iterator: public std::iterator<
                        std::forward_iterator_tag, // iterator_category
                        long,                      // value_type
                        long,                      // difference_type
                        const long*,               // pointer
                        const long&                // reference
                                      >{
        long num = FROM;
    public:
        iterator(long _num = 0) : num(_num) {}
        iterator& operator++() {num = TO >= FROM ? num + 1: num - 1; return *this;}
        iterator operator++(int) {iterator retval = *this; ++(*this); return retval;}
        bool operator==(iterator other) const {return num == other.num;}
        bool operator!=(iterator other) const {return !(*this == other);}
        long operator*() {return num;}
    };
    iterator begin() {return FROM;}
    iterator end() {return TO >= FROM? TO+1 : TO-1;}
};

(Code von http://en.cppreference.com/w/cpp/iterator/iterator mit Erlaubnis des ursprünglichen Autors).

Ohne std::iterator

template<long FROM, long TO>
class Range {
public:
    class iterator {
        long num = FROM;
    public:
        iterator(long _num = 0) : num(_num) {}
        iterator& operator++() {num = TO >= FROM ? num + 1: num - 1; return *this;}
        iterator operator++(int) {iterator retval = *this; ++(*this); return retval;}
        bool operator==(iterator other) const {return num == other.num;}
        bool operator!=(iterator other) const {return !(*this == other);}
        long operator*() {return num;}
        // iterator traits
        using difference_type = long;
        using value_type = long;
        using pointer = const long*;
        using reference = const long&;
        using iterator_category = std::forward_iterator_tag;
    };
    iterator begin() {return FROM;}
    iterator end() {return TO >= FROM? TO+1 : TO-1;}
};
31
Amir Kirsh

Option 3 ist eine streng typisierende Version von Option 1, da Sie immer das gleiche typedefs schreiben müssen, aber zusätzlich iterator_traits<X> Einschließen müssen.

Option 2 ist als Lösung nicht brauchbar. Sie können einige Typen ableiten (z. B. reference ist nur decltype(*it)), aber Sie können nicht iterator_category Ableiten. Sie können input_iterator_tag Und forward_iterator_tag Nicht einfach durch das Vorhandensein von Operationen unterscheiden, da Sie nicht reflexartig überprüfen können, ob der Iterator die Multipass-Garantie erfüllt. Außerdem können Sie nicht wirklich zwischen diesen und output_iterator_tag Unterscheiden, wenn der Iterator eine veränderbare Referenz liefert. Sie müssen explizit irgendwo bereitgestellt werden.

Das lässt Option 1 übrig. Ich denke, wir sollten uns nur daran gewöhnen, das gesamte Boilerplate zu schreiben. Zum einen begrüße ich unsere neuen Oberherren des Karpaltunnels.

30
Barry