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
typedef
s 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:
typedef
s enthaltenauto
verwenden, anstatt vom Iterator abhängig zu sein, um Typen zu deklarierenstd::iterator_traits
aktualisiert werden kann, ohne von std::iterator
Zu erbenKann mir jemand erklären, welche dieser Optionen ich erwarten sollte, wenn ich benutzerdefinierte Iteratoren mit Blick auf c ++ 17 Kompatibilität entwerfe?
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.
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).
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;}
};
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.