Ich habe eine benutzerdefinierte Containerklasse, für die ich die Klassen iterator
und schreiben möchte const_iterator
.
Ich habe das noch nie gemacht und keine passende Anleitung gefunden. Was sind die Richtlinien für die Erstellung von Iteratoren und worauf sollte ich achten?
Ich möchte auch Code-Duplikate vermeiden (ich fühle das const_iterator
und iterator
teile viele Dinge; sollte eine Unterklasse die andere sein?).
Fußnote: Ich bin mir ziemlich sicher, dass Boost etwas hat, um dies zu erleichtern, aber ich kann es hier aus vielen dummen Gründen nicht verwenden.
c++
iterator
const-iterator
ereOn
quelle
quelle
Antworten:
std::iterator
mitrandom_access_iterator_tag
.Diese Basisklassen alle Typdefinitionen von STL erforderlich definieren und anderer Arbeit verrichten.Um eine Codeduplizierung zu vermeiden, sollte die Iteratorklasse eine Vorlagenklasse sein und durch "Werttyp", "Zeigertyp", "Referenztyp" oder alle (abhängig von der Implementierung) parametrisiert werden. Beispielsweise:
Hinweis-
iterator_type
undconst_iterator_type
Typdefinitionen: Dies sind Typen für Ihre Nicht-Konstanten- und Konstanten-Iteratoren.Siehe auch: Standardbibliotheksreferenz
BEARBEITEN:
std::iterator
ist seit C ++ 17 veraltet. Eine entsprechende Diskussion finden Sie hier .quelle
random_access_iterator
ist nicht im Standard und die Antwort behandelt nicht die Konvertierung von Mutable zu Const . Sie möchten wahrscheinlich von erben, zstd::iterator<random_access_iterator_tag, value_type, ... optional arguments ...>
.RefType operator*() { ... }
, bin ich einen Schritt näher - aber es hilft nicht, weil ich noch braucheRefType operator*() const { ... }
.std::iterator
wird in C ++ 17 zur Verwerfung vorgeschlagen .std::iterator
wurde veraltetIch werde Ihnen zeigen, wie Sie Iteratoren für Ihre benutzerdefinierten Container einfach definieren können, aber nur für den Fall, dass ich eine C ++ 11-Bibliothek erstellt habe, mit der Sie problemlos benutzerdefinierte Iteratoren mit benutzerdefiniertem Verhalten für jeden Containertyp erstellen können, unabhängig davon nicht zusammenhängend.
Sie finden es auf Github
Hier sind die einfachen Schritte zum Erstellen und Verwenden von benutzerdefinierten Iteratoren:
typedef blRawIterator< Type > iterator;
typedef blRawIterator< const Type > const_iterator;
iterator begin(){return iterator(&m_data[0]);};
const_iterator cbegin()const{return const_iterator(&m_data[0]);};
Schließlich zur Definition unserer benutzerdefinierten Iteratorklassen:
ANMERKUNG: Bei der Definition von benutzerdefinierten Iteratoren leiten wir die Standard-Iteratorkategorien ab, um STL-Algorithmen über den von uns erstellten Iteratortyp zu informieren.
In diesem Beispiel definiere ich einen Iterator mit wahlfreiem Zugriff und einen Iterator mit umgekehrtem wahlfreiem Zugriff:
Jetzt irgendwo in Ihrer benutzerdefinierten Containerklasse:
quelle
m_data[m_size]
ist UB. Sie können es einfach beheben, indem Sie es durch ersetzenm_data+m_size
. Bei Reverse-Iteratoren sind beidem_data[-1]
undm_data-1
falsch (UB). Um reverse_iterators zu reparieren, müssen Sie den "Zeiger auf den nächsten Elementtrick" verwenden.Sie vergessen oft, dass
iterator
das konvertieren muss,const_iterator
aber nicht umgekehrt. Hier ist ein Weg, dies zu tun:In der obigen Anmerkung, wie
IntrusiveSlistIterator<T>
konvertiert zuIntrusiveSlistIterator<T const>
. Wenn diesT
bereits der Fall ist, wirdconst
diese Konvertierung nie verwendet.quelle
const
in nicht zu konvertierenconst
.IntrusiveSlistIterator<T const, void>::operator IntrusiveSlistIterator<T const, void>() const
?enable_if
könnte es beheben, aber ...Boost hat etwas zu helfen: die Boost.Iterator-Bibliothek.
Genauer gesagt diese Seite: boost :: iterator_adaptor .
Sehr interessant ist das Tutorial-Beispiel, das eine vollständige Implementierung eines benutzerdefinierten Typs von Grund auf zeigt.
Der Hauptpunkt ist, wie bereits erwähnt, die Verwendung einer einzelnen Vorlagenimplementierung und
typedef
dieser.quelle
// a private type avoids misuse
enabler
ist niemals als Anbieter durch den Anrufer gedacht, daher vermute ich, dass sie es privat machen, um zu vermeiden, dass Leute versehentlich versuchen, es weiterzugeben. Ich glaube nicht, dass es ein Problem schaffen könnte, es tatsächlich zu bestehen, da der Schutz darin liegtenable_if
.Ich weiß nicht, ob Boost etwas hat, das helfen würde.
Mein bevorzugtes Muster ist einfach: Nehmen Sie ein Vorlagenargument
value_type
, das entweder const qualifiziert ist oder nicht. Bei Bedarf auch ein Knotentyp. Dann passt alles zusammen.Denken Sie daran, alles zu parametrisieren (template-ize), was erforderlich ist, einschließlich des Kopierkonstruktors und
operator==
. Zum größten Teil erzeugt die Semantik vonconst
korrektes Verhalten.quelle
cur
fehlerhaft sind, da ich sie einfach nicht illustriert habe, aber es könnte ein Problem geben, auf das vom Iterator mit entgegengesetzter Konstanz zugegriffen wird. Die Lösung, die mir in den Sinn kommt, istfriend my_container::const_iterator; friend my_container::iterator;
, aber ich glaube nicht, dass ich es vorher so gemacht habe ... trotzdem funktioniert diese allgemeine Gliederung.friend class
in beiden Fällen.Es gibt viele gute Antworten, aber ich habe einen von mir verwendeten Vorlagen-Header erstellt , der sehr präzise und einfach zu verwenden ist.
Um Ihrer Klasse einen Iterator hinzuzufügen, muss nur eine kleine Klasse geschrieben werden, um den Status des Iterators mit 7 kleinen Funktionen darzustellen, von denen 2 optional sind:
Dann können Sie es so verwenden, wie Sie es von einem STL-Iterator erwarten würden:
Ich hoffe, es hilft.
quelle