Writing iterators yourself is hardly ever beautiful. The most preliminary way to add an iterator to your classes is to reuse an existing one. You should know that pointers, for example, are just as good as C ++ iterators, so there are many ways to provide an iterator without having to write your own.
This is basically how C ++ works in different ways. He is trying to make the language accessible and easy for end users, putting a lot of work on library writers. That is, library writers can write all unpretentious things, so end users do not need this. Iterators are usually part of the library.
Having said that, here comes the real ugly part:
To be able to write your own iterators, here are some things you need to know about.
Typical Features:
Typical traits are a simple mechanism for adding additional information to types in C ++, which works even with types that cannot be changed themselves. For example, it is important for an iterator to know that it is iterating (i.e. contained). The way this information is obtained for a given iterator is largely dependent on the iterator. For iterators that are actually objects, you can add typedefs to the class and use them, but for iterators that are pointers, you need to deduce them from the pointer type. To make this possible, information is stored in a type type, so there is a place where the code can view this information. This is a sign of the type std::iterator_traits .
std::iterator_traits works on anything that comes from the std::iterator template, as well as any pointer without any settings. Therefore, it is often better to use std::iterator as the basis, so as not to write a specialization. If you cannot do this, it is still possible to provide the necessary traits, but it will be more difficult.
Tag classes and iterator types:
There are several different types of iterators available in C ++ that have different behaviors and may / may not do many different things. Take a look at http://cplusplus.com/reference/std/iterator/ to find out which iterators are available and what they can do. Diagrams are not intended for an object-oriented approach (i.e. input_iterator is neither a sub nor a base class of a forward_iterator ), but rather as an API output type. That is, you can use all the algorithms that were written for the input-iterator, also using the forwarding iterator. In the table on the page you will find out what methods you need to provide for each category.
Since these categories are not actually subclasses of each other (they should not be, especially when sending from different types of collections), a different mechanism is used to determine the capabilities of each iterator. An empty tag class is also included in std::iterator_traits , which describes each iterator that reports what this iterator can do and what it cannot do. If you are not writing your own traits, you need to provide this tag class to the std::iterator template after creating the instance.
Example:
This example is taken from the cplusplus.com section on iterators:
class myiterator : public iterator<input_iterator_tag, int> { int* p; public: myiterator(int* x) :p(x) {} myiterator(const myiterator& mit) : p(mit.p) {} myiterator& operator++() {++p;return *this;} myiterator operator++(int) {myiterator tmp(*this); operator++(); return tmp;} bool operator==(const myiterator& rhs) {return p==rhs.p;} bool operator!=(const myiterator& rhs) {return p!=rhs.p;} int& operator*() {return *p;} };
This iterator doesn't really make sense, since it only wraps a pointer, which can also be used directly. However, this can serve as an explanation. The std::iterator is input_iterator from std::iterator as input_iterator by supplying the appropriate tag. It is also reported that this iterator iterates over int s. All other types that are required by difference_type , reference , poiner , etc., are automatically output by the template. In some cases, it may make sense to change some of these types manually (for example, a std::shared_ptr sometimes used as a pointer ). In addition, the traits necessary for this iterator will exist automatically, since it is already obtained from std::iterator , and std::iterator_traits knows where to find all the necessary information.