    Section ref(ITERATORS) iterators as used by generic algorithms were
introduced. We've seen that several types of iterators were distinguished:
InputIterators, ForwardIterators, OutputIterators, BidirectionalIterators,
RandomAccessIterators, and ContiguousIterators.

    To ensure that an object of a class is interpreted as a particular type of
iterator, the class must be designed so that it satisfies the requirements of
that type of iterator. The interface of a class implementing an iterator uses
    hi(iterator_tag) em(iterator_tags), which are available when including the
tthi(iterator) header file.

    Iterator classes should provide the following tt(using) declarations:
    itemization(
    iti(iterator_category), specifying the iterator's tag, as in
       verb(   using iterator_category = std::random_access_iterator_tag;)
    iti(differenct_type), defining the type representing differences between
        pointers, commonly:
       verb(   using difference_type = std::ptrdiff_t;)
    iti(value_type), defining the data type of a dereferenced iterator. E.g.,
       verb(   using value_type = std::string;)
    iti(pointer), defining the iterator's pointer type which can also be
        defined as a tt(const) pointer. E.g.,
       verb(   using pointer = value_type *; // or maybe:
    using pointer = value_type const *;)
    iti(reference), defining the iterator's pointer type which can also be
        defined as a tt(const) pointer. E.g.,
       verb(   using reference = value_type &; // or maybe:
    using reference = value_type const &;)
    )

    These em(using declarations) are commonly encountered in the public
sections of iterators, which therefore might be defined as structs.
 hi(iterator: generic design)hi(iterator: 'using' declarations) Here's an
example of how an iterator's interface could be designed:
    verb(    struct Iterator
    {
        using iterator_category = std::random_access_iterator_tag;
        using difference_type   = std::ptrdiff_t;
        using value_type        = std::string;
        using pointer           = value_type *;
        using reference         = value_type &;
 
        friend bool operator==(iterator const &lhs, iterator const &rhs);
        friend auto operator<=>(Iterator const &lhs, Iterator const &rhs);
        friend int operator-(Iterator const &lhs, Iterator const &rhs);
        friend Iterator operator+(Iterator const &lhs, int step);
        friend Iterator operator-(Iterator const &lhs, int step);
 
    private:
        // private data members
 
    public:
        // public members

    private:
        // private support members
    })
    In section ref(ITERATORS) the characteristics of iterators were
discussed. All iterators
        hi(iterator: common characteristics)
        hi(iterator: requirements)
    should support (using tt(Iterator) as the generic name of the designed
iterator class and tt(Type) to represent the (possibly tt(const), in which
case the associated operator should be a tt(const) member as well) data type to
which tt(Iterator) objects refer):
    itemization(
    it() a prefix increment operator (tt(Iterator &operator++()));
    it() a dereference operator (tt(Type &operator*()));
    it() a 'pointer to' operator (tt(Type *operator->()));
    it() comparison operators testing the (in)equality of two iterator objects
(preferably free operators, like tt(bool operator==(Iterator const &lhs,
        Iterator const &rhs)), tt(bool operator!=((Iterator const &lhs,
        Iterator const &rhs)))). Instead of tt(operator!=) the `spaceship
        operator (tt(operator<=>)) can also be used, allowing ordering of the
        iterators.
    )

    When
        hi(iterator: and generic algorithms)
    iterators are used in the context of generic algorithms they must meet
additional requirements. These are required because generic algorithms perform
checks on the types of the iterators they receive. Simple pointers are usually
accepted, but if an iterator-object is used the generic algorithm might
require the iterator to specify what type of iterator it represents.

    The type of iterator that is implemented is indicated by the class's
tt(iterator_category). The six standard iterator categories are:
    itemization(
    itht(input_iterator_tag)(std::input_iterator_tag). This tag is used when
the iterator class defines an i(InputIterator).  Iterators of this type allow
reading operations, iterating from the first to the last element of the series
to which the iterator refers.

    The InputIterator  dereference operator could be declared as follows:
        verb(    value_type const &operator*() const;)
    or, in a concrete implementation the data type specified at tt(using
value_type = ...) could be used instead to tt(value_type).  Except for the
standard operators there are no further requirements for InputIterators.

    itht(output_iterator_tag)(std::output_iterator_tag). This tag is used when
the iterator class defines an i(OutputIterator).  Iterators of this type allow
assignments to their dereferenced iterators. Therefore, the OutputIterator
dereference operator is usually declared as:
        verb(    value_type &operator*();)
Except for the standard operators there are no further requirements for
OutputIterators.

    itht(forward_iterator_tag)(std::forward_iterator_tag). This tag is used
when the iterator class defines a i(ForwardIterator).  Iterators of this type
allow reading em(and) assignment operations, iterating from the first to the
last element of the series to which the iterator refers. Therefore, the
ForwardIterator dereference operator should be declared as follows:
        verb(    value_type &operator*();)
    and it could also declare an overloaded tt(operator*() const):    
        verb(    value_type const &operator*() const;)
Except for the standard operators there are no further requirements for
ForwardIterators.

    itht(bidirectional_iterator_tag)(std::bidirectional_iterator_tag). This
tag is used when the iterator class defines a
i(BidirectionalIterator). Iterators of this type allow reading em(and)
assignment operations, iterating step by step, possibly in either direction,
over all elements of the series to which the iterator refers.

    The Bidirectional dereference operator should allow assignment to the data
its dereference operator refers to and it should allow stepping
backward. BidirectionalIterator should therefore, in addition to the standard
operators required for iterators, offer the following operators:
        verb(    value_type &operator*(); // any maybe its const-overload
    Iterator &operator--();)

itht(random_access_iterator_tag)(std::random_access_iterator_tag). This tag is
used when the iterator class defines a i(RandomAccessIterator).  These
iterators are like Bidirectional iterators, but in addition allow stepping to
the next iterator using tt(int) step sizes (the resulting iterator may point
to a location outside of the valid iterator range. Like other iterators,
RandomAccessIterators do not check the validity of the obtained iterator).

    In addition to the members offered by the BidirectionalIterator, 
RandomAccessIterators provide the following operators:
    itemization(
    itt(Type operator-(Iterator const &rhs) const), returning the number of
data elements between the current and tt(rhs) iterator (returning a negative
value if tt(rhs) refers to a data element beyond the data element tt(this)
iterator refers to);
    itt(Iterator operator+(int step) const), returning an iterator referring
to a data element tt(step) data elements beyond the data element tt(this)
iterator refers to;
    itt(Iterator operator-(int step) const), returning an iterator referring
to a data element tt(step) data elements before the data element tt(this)
iterator refers to;
    itt(bool operator<(Iterator const &rhs) const), returning tt(true) if
the data element tt(this) iterator refers to is located before the data
element the tt(rhs) iterator refers to.
        )
itht(contiguous_iterator_tag)(std::contiguous_iterator_tag). This tag is
used when the iterator class defines a i(ContiguousIterator).  These iterators
offer the same facilities as the RandomAccessIterators, but it is the
responsibility of the software engineer to ensure that the elements to which
the iterators refer are stored contiguously in memory.
    )

    Each iterator tag assumes that a certain set of operators is
available. The em(RandomAccessIterators) ant tt(ContiguousIterators) are the
most complex, as other iterators only offer subsets of their facilities.

    Note that iterators are always defined over a certain range
(rangett(begin, end)). Increment and decrement operations may result in
undefined behavior of the iterator if the resulting iterator value would refer
to a location outside of this range.

    Normally iterators of classes merely accesses the data elements of those
classes. Internally, an iterator might use an ordinary pointer but it is
hardly ever necessary for the iterator to allocate memory. Therefore,
iterators normally don't need to define copy constructors and assignment
operators. For the same reason iterators usually don't require destructors.

    Most classes offering members returning iterators do so by having members
construct the required iterators that are thereupon returned as objects. As
the em(caller) of these member functions only has to em(use) or sometimes
em(copy) the returned iterator objects, there is usually no need to provide
publicly available constructors, except for the copy constructor (which is
available by default). Therefore additional constructors are usually defined as
em(private) or em(protected) members. To allow an outer class to create
iterator objects, the iterator class usually declares the outer class as its
em(friend).

    In the following sections a em(RandomAccessIterator), and a em(reverse
RandomAccessIterator) is covered. The container class for which a random
access iterator must be developed may actually store its data elements in many
different ways (e.g., using containers or pointers to pointers).  Section
ref(CONCRETEITER) contains an example of a self-made template iterator class.

The random access iterator developed in the next sections reaches data
elements that are only accessible through pointers. The iterator class is
designed as an inner class of a class derived from a vector of string
pointers.
