Am 21. März st stimmte der Normenausschuss der deprecation von genehmigen std::iterator
vorgeschlagen in P0174 :
Die lange Folge von ungültigen Argumenten ist für den Leser viel weniger klar als die bloße
typedef
Angabe der erwarteten s in der Klassendefinition selbst. Dies ist der Ansatz des aktuellen Arbeitsentwurfs nach dem festgelegten Musterc ++ 14
Vor c ++ 17Die Vererbung von std::iterator
wurde ermutigt, die Langeweile bei der Implementierung des Iterator-Boilerplates zu beseitigen. Die Abwertung erfordert jedoch eines der folgenden Dinge:
- Ein Iterator-Boilerplate muss nun alle erforderlichen
typedef
s enthalten - Algorithmen, die mit Iteratoren arbeiten, müssen jetzt verwendet werden,
auto
anstatt vom Iterator abhängig zu sein, um Typen zu deklarieren - Loki Astari hat vorgeschlagen ,
std::iterator_traits
dass aktualisiert werden kann, um zu funktionieren, ohne von zu erbenstd::iterator
Kann mir jemand erklären, welche dieser Optionen ich erwarten sollte, wenn ich benutzerdefinierte Iteratoren mit Blick auf entwerfe c ++ 17 Kompatibilität?
Antworten:
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 verwendete Code
std::iterator
auf den darunter liegenden Code festgelegt.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 Genehmigung 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;} };
quelle
operator*
sollte zurückkehrenreference
, Sie brauchen eineoperator->
Rückgabe apointer
, obwohl es für along
Option 3 ist eine streng typisierende Version von Option 1, da Sie alle gleich schreiben müssen,
typedefs
aber zusätzlich umbrechen müsseniterator_traits<X>
.Option 2 ist als Lösung unrentabel. Sie können einige Typen ableiten (z. B.
reference
ist nurdecltype(*it)
), aber Sie können nicht ableiteniterator_category
. Sie können nicht zwischeninput_iterator_tag
undforward_iterator_tag
einfach durch das Vorhandensein von Operationen unterscheiden, da Sie nicht reflexartig prüfen können, ob der Iterator die Multipass-Garantie erfüllt. Außerdem können Sie nicht wirklich zwischen diesen unterscheiden undoutput_iterator_tag
ob der Iterator eine veränderbare Referenz liefert. Sie müssen explizit irgendwo angegeben werden.Damit bleibt Option 1. Ich denke, wir sollten uns nur daran gewöhnen, die gesamte Boilerplate zu schreiben. Zum einen begrüße ich unsere neuen Karpaltunnel-Oberherren.
quelle
std::iterator
Sie tun, wirklich gefällt , können Sie trivial Ihre eigene Version schreiben. Das Risiko eines Karpaltunnels ist also stark übertrieben.std::iterator
zugunsten von ... jedem zu schreiben, der jetzt seine eigene Kopie schreibtstd::iterator
, um dieses Problem trotzdem zu lösen.