Mam niestandardową klasę kontenera, dla której chciałbym napisać klasy iterator
i const_iterator
.
Nigdy wcześniej tego nie robiłem i nie znalazłem odpowiedniego poradnika. Jakie są wytyczne dotyczące tworzenia iteratorów i o czym powinienem wiedzieć?
Chciałbym również uniknąć powielania kodu (czuję to const_iterator
i iterator
dzielę się wieloma rzeczami; czy jedna z nich należy podklasować?
Uwaga: Jestem całkiem pewien, że Boost ma coś na to, ale nie mogę tego tutaj użyć z wielu głupich powodów.
c++
iterator
const-iterator
ereOn
źródło
źródło
Odpowiedzi:
std::iterator
pomocąrandom_access_iterator_tag
. Te klasy podstawowe definiują wszystkie definicje typów wymagane przez STL i wykonują inną pracę.Aby uniknąć powielania kodu, klasa iteratora powinna być klasą szablonu i powinna być sparametryzowana według „typu wartości”, „typu wskaźnika”, „typu odniesienia” lub wszystkich (zależnie od implementacji). Na przykład:
Uwaga
iterator_type
iconst_iterator_type
definicje typów: są to typy dla iteratorów non-const i const.Zobacz także: standardowe odwołanie do biblioteki
EDYCJA:
std::iterator
jest przestarzała od C ++ 17. Zobacz odpowiednią dyskusję tutaj .źródło
random_access_iterator
nie ma jej w standardzie, a odpowiedź nie obsługuje konwersji zmiennej na stałą . Prawdopodobnie chcesz dziedziczyć, na przykładstd::iterator<random_access_iterator_tag, value_type, ... optional arguments ...>
.RefType operator*() { ... }
, jestem o krok bliżej - ale to nie pomaga, ponieważ wciąż potrzebujęRefType operator*() const { ... }
.std::iterator
jest proponowane do wycofania w C ++ 17 .std::iterator
jest przestarzałePokażę ci, jak łatwo zdefiniować iteratory dla niestandardowych kontenerów, ale na wszelki wypadek utworzyłem bibliotekę c ++ 11, która pozwala łatwo tworzyć niestandardowe iteratory z niestandardowym zachowaniem dla dowolnego typu kontenera, ciągłego lub niesąsiadujące.
Możesz go znaleźć na Github
Oto proste kroki tworzenia i używania niestandardowych iteratorów:
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]);};
Wreszcie, aby zdefiniować nasze niestandardowe klasy iteratorów:
UWAGA: Definiując niestandardowe iteratory, wywodzimy się ze standardowych kategorii iteratorów, aby algorytmy STL znały rodzaj iteratora, który stworzyliśmy.
W tym przykładzie definiuję iterator losowego dostępu i odwrotny iterator losowego dostępu:
Teraz gdzieś w niestandardowej klasie kontenera:
źródło
m_data[m_size]
to UB. Możesz to po prostu naprawić, zastępując gom_data+m_size
. Na odwrotnej iteratorami, jakm_data[-1]
im_data-1
nie są poprawne (UB). Aby naprawić reverse_iterators, musisz użyć „wskaźników do następnego elementu trick”.Często zapominają, że
iterator
muszą przejść na,const_iterator
ale nie na odwrót. Oto sposób, aby to zrobić:W powyższym ogłoszeniu
IntrusiveSlistIterator<T>
konwertuje się naIntrusiveSlistIterator<T const>
. JeśliT
jest już,const
ta konwersja nigdy nie zostanie wykorzystana.źródło
const
na non-const
.IntrusiveSlistIterator<T const, void>::operator IntrusiveSlistIterator<T const, void>() const
?enable_if
Może go naprawić, ale ...Boost ma coś, co może pomóc: bibliotekę Boost.Iterator.
Dokładniej ta strona: boost :: iterator_adaptor .
Bardzo interesujący jest samouczek, który pokazuje kompletną implementację od zera dla niestandardowego typu.
Najważniejszym punktem, jak już wspomniano, jest użycie implementacji jednego szablonu i
typedef
to.źródło
// a private type avoids misuse
enabler
nigdy nie ma być dostawcą przez dzwoniącego, więc domyślam się, że zapewniają prywatność, aby uniknąć przypadkowej próby przekazania go przez osoby. Nie sądzę, że może to stworzyć jakikolwiek problem, aby go przekazać, ponieważ polega na ochronieenable_if
.Nie wiem, czy Boost ma coś, co by pomogło.
Mój preferowany wzorzec jest prosty: weź argument szablonu, który jest równy
value_type
albo const kwalifikowany, albo nie. W razie potrzeby także typ węzła. No cóż, wszystko wpada na miejsce.Pamiętaj tylko, aby sparametryzować (szablon-ize) wszystko, co musi być, w tym konstruktor kopii i
operator==
. W większości semantykaconst
spowoduje prawidłowe zachowanie.źródło
cur
z dostępem z iteratora przeciwnej ciągłości. Przychodzi mi na myśl rozwiązaniefriend my_container::const_iterator; friend my_container::iterator;
, ale nie sądzę, że tak to robiłem wcześniej… w każdym razie ten ogólny zarys działa.friend class
w obu przypadkach.Istnieje wiele dobrych odpowiedzi, ale utworzyłem nagłówek szablonu, którego używam, który jest dość zwięzły i łatwy w użyciu.
Aby dodać iterator do swojej klasy, wystarczy napisać małą klasę reprezentującą stan iteratora za pomocą 7 małych funkcji, z których 2 są opcjonalne:
Następnie możesz użyć go tak, jak można oczekiwać od iteratora STL:
Mam nadzieję, że to pomoże.
źródło