\cleardoublepage
\chapter{Iterators}
\label{chapterIterators}
Iterators are a generalization of pointers that allow a programmer to
work with different data structures (containers) in a uniform manner.
An iterator is the glue that allows to write a single implementation of
an algorithm that will work for data contained in an array, a list or
some other container -- even a container that did not yet exist when the
algorithm was implemented.
An iterator is a concept, not a programming language construct. It can
be seen as a set of requirements. A type is an iterator if it satisfies
those requirements. So, for instance, a pointer to an element of an
array is an iterator. We will check this later.
Depending on the operations defined for an iterator, there are five
categories: {\em input, output, forward, bidirectional} and {\em
random access iterators}. We first have to introduce some terminology.
{\bf Mutable versus constant:}
There is an additional attribute that forward, bidirectional and
random access iterators might have, that is, they can be {\em mutable}
or {\em constant} depending on whether the result of the
\ccTexHtml{{\tt operator }\raisebox{-1mm}{*}}{operator *}
behaves as a reference or as a reference to a constant.
{\bf Past-the-end value:}
Just as a regular pointer to an array guarantees that there is a
pointer value pointing past the last element of the array, so for any
iterator type there is an iterator value that points past the last
element of a corresponding container. These values are called {\em
past-the-end} values. Values of the iterator for which the
\ccTexHtml{{\tt operator }\raisebox{-1mm}{*}}{operator *}
is defined are called {\em dereferenceable}.
The library never assumes that past-the-end values are
dereferenceable.
{\bf Reachability} An iterator $j$ is called {\em reachable} from an
iterator $i$ if and only if there is a finite sequence of applications
of \ccStyle{operator}$\!+\!+$ to $i$ that makes $i == j$. If $i$ and
$j$ refer to the same container, then either $j$ is reachable from
$i$, or $i$ is reachable from $j$, or both ($i == j$).
{\bf Range:}
Most of the library's algorithmic templates that operate on data
structures have interfaces that use {\em ranges}. A range is a pair of
iterators that designate the beginning and end of the computation. A
range $\left[i, i\right)$ is an {\em empty range}; in general, a range
$\left[i, j\right)$ refers to the elements in the data structure
starting with the one pointed to by $i$ and up to but not including the
one pointed to by $j$. Range $\left[i, j\right)$ is valid if and only if $j$
is reachable from $i$. The result of the application of the
algorithms in the library to invalid ranges is undefined.
\medskip
As we mentioned in the introduction we are a little bit sloppy in the
presentation of \stl, in order to make it easier to understand. A
class is said to be an iterator if it fulfills a set of requirements.
In the following sections we do not present the requirements, but we
state properties that are true, if the requirements are fulfilled.
The difference is best seen by an example: we write that the return
value of the test for equality returns a \ccStyle{bool}, but the
requirement is only that the return value is convertible to \ccStyle{bool}.
\newpage
\begin{ccClass}{forward_iterator}
\ccSection{Forward Iterator}
\ccDefinition
A class \ccClassName\ that satisfies the requirements of a forward iterator
for the value type \ccStyle{T}, supports the following operations.
\ccCreation
\ccCreationVariable{it}
\ccConstructor{iterator();}
{}
\ccConstructor{iterator(const iterator& it1);}
{}
\ccOperations
\ccMethod{iterator& operator=(const iterator &it1);}
{Assignment.}
\ccMethod{bool operator==(const iterator &it1) const;}
{Test for equality: Two iterators are equal if they refer to the same item.}
\ccMethod{bool operator!=(const iterator &it1) const;}
{Test for inequality. The result is the same as \ccStyle{!(it == it1)}.}
\ccMethod{T& operator*();}
{Returns the value of the iterator.
If \ccClassName\ is mutable \ccStyle{*it = t} is valid.
\ccPrecond \ccVar\ is dereferenceable.}
\ccMethod{iterator& operator++();}
{Prefix increment operation.
\ccPrecond \ccVar\ is dereferenceable.}
\ccMethod{const iterator& operator++(int);}
{Postfix increment operation. The result is the same as that of
\ccStyle{iterator tmp = it; ++it; return tmp;}.
\ccPrecond \ccVar\ is dereferenceable.}
\end{ccClass}
\begin{ccClass}{bidirectional_iterator}
\ccSection{Bidirectional Iterator}
\ccDefinition
A class \ccClassName\ that satisfies the requirements of a bidirectional
iterator for the value type \ccStyle{T}, supports the following operations
in addition to the operations supported by a forward iterator.
\ccCreationVariable{it}
\ccOperations
\ccMethod{iterator& operator--();}
{Prefix decrement operation.
\ccPrecond \ccVar\ is dereferenceable.}
\ccMethod{const iterator& operator--(int);}
{Postfix decrement operation. The result is the same as that of
\ccStyle{iterator tmp = it; --it; return tmp;}.
\ccPrecond \ccVar\ is dereferenceable.}
\end{ccClass}
\newpage
\begin{ccClass}{random_access_iterator}
\ccSection{Random Access Iterator}
\ccDefinition
A class \ccClassName\ that satisfies the requirements of a random access
iterator for the value type \ccStyle{T}, supports the following operations
in addition to the operations supported by a bidirectional iterator.
\ccCreationVariable{it}
\vfill
\ccOperations
\ccMethod{iterator& operator+=(int n);}
{The result is the same as if the prefix increment operation
was applied $n$ times, but it is computed in constant time.}
\ccMethod{iterator operator+(int n);}
{Same as above, but returns a new iterator.}
\ccFunction{iterator operator+(int n, iterator it);}
{Same as above.}
\ccMethod{iterator& operator-=(int n);}
{The result is the same as if the prefix decrement operation
was applied $n$ times, but it is computed in constant time.}
\ccMethod{iterator operator-(int n);}
{Same as above, but returns a new iterator.}
\ccMethod{int operator-(const iterator &it1);}
{The result \ccStyle{n} is such that \ccStyle{it1 + n ==} \ccVar.
\ccPrecond \ccVar\ is reachable from \ccStyle{it1}.}
\ccMethod{T& operator[](int n);}
{Returns \ccStyle{*(it + n)}.
\ccPrecond \ccStyle{*(it + n)} is dereferenceable}
\ccMethod{bool operator<(iterator it1);}
{$<$ is a total ordering relation.}
\ccMethod{bool operator>(iterator it1);}
{$>$ is a total ordering relation opposite to \ccStyle{<}.}
\ccMethod{bool operator<=(iterator it1);}
{Result is the same as \ccStyle{! it > it1}.}
\ccMethod{bool operator>=(iterator it1);}
{Result is the same as \ccStyle{! it < it1}.}
\vfill
\ccExample
We promised to show why a pointer (in this case \ccStyle{V*}) is a random access
iterator. In order to show that it supports the required operations, we
give a program that uses them. Not all operations are used --- so it is not a
complete proof --- but hopefully enough to convince the reader.
Notice that the program is nothing special. It was valid \CC\ (with minor
changes even valid C), long before the iterator concept was invented!
The comments point to the requirements.
\newpage
\begin{cprog}
const int N = 10;
struct V {
float val;
};
float fill_and_add(V *b, V *e)
{
V *c; /* creation, first way (ForwardIterator) */
float sum;
for (int i=0; i
float fill_and_add(RandomAccessIterator b, RandomAccessIterator e)
{
RandomAccessIterator c;
float sum;
for (int i=0; i it(cin),
end();
double sum = 0.0;
while(it != end){
sum += *it;
++it;
}
cout << sum << endl;
}
\end{cprog}
\end{ccClass}
\begin{ccClass}{output_iterator}
\ccSection{Output Iterator}
\ccDefinition
A class \ccClassName\ that satisfies the requirements of an output iterator
for the value type \ccStyle{T}, supports the following operations.
Algorithms on input iterators should never attempt to pass through the
same iterator twice. They should be single pass algorithms.
\ccCreation
\ccCreationVariable{it}
\ccConstructor{iterator();}
{}
\ccConstructor{iterator(const iterator& it1);}
{}
\ccOperations
\ccMethod{iterator& operator=(const iterator &it1);}
{Assignment.}
\ccMethod{bool operator==(const iterator &it1) const;}
{Test for equality: Two iterators are equal if they refer to the same item.}
\ccMethod{bool operator!=(const iterator &it1) const;}
{Test for inequality. The result is the same as \ccStyle{!(it == it1)}.}
\ccMethod{T& operator*();}
{Returns a reference to the value of the iterator. This operator can
only be used in order to assign a value to this reference.}
\ccMethod{iterator& operator++();}
{Prefix increment operation.
\ccPrecond \ccVar\ is dereferenceable.}
\ccMethod{void operator++(int);}
{Postfix increment operation. The result is the same as that of
\ccStyle{iterator tmp = it; ++it; return tmp;}.
\ccPrecond \ccVar\ is dereferenceable.}
\ccExample
The following code fragment reads numbers of the type \ccc{double}
from \ccc{cin} and computes their sum. The {\sc Stl} provides an
\ccc{ostream_iterator} that fulfills the output iterator requirements.
As the iterator is a kind of file pointer it should be clear
why only single pass algorithms can be applied on this iterator.
\begin{cprog}
{
ostream_iterator it(cout);
for(int r = 0; r < 10; r++){
*it = 3.1415 * r * r;
++it;
}
}
\end{cprog}
The above code fragment is equivalent to:
\begin{cprog}
{
for(int r = 0; r < 10; r++){
cout << 3.1415 * r * r;
}
}
\end{cprog}
The use of output iterators is better illustrated with a function
that can write into arbitrary containers:
\begin{cprog}
template < class OutputIterator >
void
generator(OutputIterator it)
{
for(int r = 0; r < 10; r++){
*it = 3.1415 * r * r;
++it;
}
}
\end{cprog}
and here comes its usage.
\begin{cprog}
{
ostream_iterator it(cout);
generator(it);
double R[10];
generator(R);
}
\end{cprog}
Note that the memory where the function \ccc{generator} writes to
must be allocated. If you want to insert the generated doubles at the end
of a list you have to use a \ccc{back_insert_iterator}. To explain
this is out of the scope of this introduction to the {\sc Stl}. Please
refer to the {\sc Stl} reference manuals.
\end{ccClass}