Some edits to Jeremy's original text

Filled out input iterator operator-> section


[SVN r10283]
This commit is contained in:
Dave Abrahams
2001-06-06 23:09:40 +00:00
parent 1c555eae91
commit b950b3afed

View File

@ -79,9 +79,12 @@ paper we present the iterator type generator from the Boost Iterator
Adaptor Library. This generator simplifies the creation of iterators; Adaptor Library. This generator simplifies the creation of iterators;
it automates the error-prone and redundant parts of the implementation it automates the error-prone and redundant parts of the implementation
and greatly simplifies the creation of iterator types that are and greatly simplifies the creation of iterator types that are
variations on other iterators (creating iterator adaptors). The variations on other iterators (adapted iterators). The Iterator
design of the Iterator Adaptor Library is an example of policy-based Adaptor Library employs template meta-programming and is an example of
design, and the implementation employs template meta-programming. policy-based design. It uses an extremeley flexible implementation
pattern which can be easily adapted to generate new representatives of
other abstract Concept families.
\end{abstract} \end{abstract}
@ -98,14 +101,27 @@ common tasks for C++ programmers. There are plenty of examples of
custom-made iterators in the literature: the custom-made iterators in the literature: the
\code{line\_iterator}~\cite{austern99:_gener_progr_stl}, \code{line\_iterator}~\cite{austern99:_gener_progr_stl},
\code{Constant\_iterator}~\cite{koenig97:_rumin_cpp}, \code{Constant\_iterator}~\cite{koenig97:_rumin_cpp},
\code{istream\_iterator} and \code{std::istream\_iterator} and
\code{ostream\_iterator}~\cite{iso98:_cpp_final_draft_standard} to \code{std::ostream\_iterator}~\cite{iso98:_cpp_final_draft_standard}n to
name a few. Also a growing number of generic iterator adaptors are name a few.
available: \code{Checked\_iter}~\cite{stroustrup00:_cpp_prog_lang},
iterators of the View Template Library~\cite{TMPW00:Weiser}, custom % right here you introduce the notion of iterator adaptor as a
and smart iterators~\cite{becker98:_smart_iteraters,TMPW00:Baus}, % byproduct of saying something else. Should say:
compound iterators~\cite{alexandrescu98:_compound_iters}, and several
iterators in the MTL~\cite{siek99:_scitools}. An iterator adaptor's job is to adapt some \code{Base} type, often
itself an iterator, to produce a new adapted iterator that conforms to
the Conceptual requirements of its iterator category.
% although this may not be the best place for it.
% I'm not sure if I changed your meaning by striking ``Also'' below:
A growing number of generic iterator adaptors are available:
\code{std::reverse\_iterator}~\cite{iso98:_cpp_final_draft_standard},
\code{Checked\_iter}~\cite{stroustrup00:_cpp_prog_lang}, iterators of
the View Template Library~\cite{TMPW00:Weiser}, custom and smart
iterators~\cite{becker98:_smart_iteraters,TMPW00:Baus}, compound
iterators~\cite{alexandrescu98:_compound_iters}, and several iterators
in the MTL~\cite{siek99:_scitools}.
For an iterator to be usable with the Standard algorithms (and other For an iterator to be usable with the Standard algorithms (and other
generic algorithms in third-party libraries), it must fulfill the generic algorithms in third-party libraries), it must fulfill the
@ -117,12 +133,11 @@ despite the fact that most iterators are conceptually simple.
\subsection{Redundant Operators} \subsection{Redundant Operators}
Perhaps the most obvious of reasons that implementing an iterator can Perhaps the most obvious reason that implementing an iterator can
be tedious is that there are lots of redundant operators. That is, be tedious is that there are lots of redundant operators. That is,
there are many operators that can be trivially defined in terms of there are many operators that can be trivially defined in terms of
other operators. For example, the \code{operator++(int)} is often best other operators. For example, the postfix \code{operator++(int)} is often best
implemented in terms of \code{operator++()} as the example below implemented in terms of prefix \code{operator++()} as shown below.
shows.
{\footnotesize {\footnotesize
\begin{verbatim} \begin{verbatim}
@ -153,17 +168,18 @@ redundant.
% \code{operator>=} % \code{operator>=}
\subsection{Delegation of Operators for Iterator Adaptors} \subsection{Delegation of Operators and Type Definitions}
It is often the case that an iterator adaptor changes the meaning of An iterator adaptor used to adapt an underlying iterator type often
one or two operators while leaving the rest of the operators defined changes the meaning of one or two operators while leaving the rest of
in the same way as the underlying iterator. This is typically the operators defined in the same way as in the base iterator. This
implemented with delegating functions. The following example shows an is typically implemented with delegating functions. The following
excerpt from an \code{indirect\_iterator} adaptor which takes an example shows an excerpt from an \code{indirect\_iterator} adaptor,
iterator over pointers and creates an iterator over the things pointed which takes an iterator over pointers or smart-pointers and creates an
to. The \code{operator*} is changed to dereference twice but all the iterator over the things pointed to. The \code{operator*} and
other operators stay the same. Writing all of the delegating functions \code{operator->} are changed to dereference twice but all the other
for the \code{indirect\_iterator} would be a tedious task. operators stay the same. Writing all of the delegating functions for
the \code{indirect\_iterator} is a tedious job.
{\footnotesize {\footnotesize
\begin{verbatim} \begin{verbatim}
@ -173,6 +189,9 @@ for the \code{indirect\_iterator} would be a tedious task.
reference operator*() const { reference operator*() const {
return **iter; // dereference twice return **iter; // dereference twice
} }
pointer operator->() const {
return &**iter; // dereference twice
}
// Delegating the implementation to the underlying iterator. // Delegating the implementation to the underlying iterator.
indirect_iterator& operator++() { ++iter; return *this; } indirect_iterator& operator++() { ++iter; return *this; }
indirect_iterator& operator--() { --iter; return *this; } indirect_iterator& operator--() { --iter; return *this; }
@ -183,6 +202,16 @@ for the \code{indirect\_iterator} would be a tedious task.
\end{verbatim} \end{verbatim}
} }
% I think it would be better to use reverse_iterator as an example
% here, because it delegates more types than indirect_iterator does.
In addition, a standard-conforming iterator must either come with a
specialization of \code{std::iterator\_traits<>} or it must define
five nested types: \code{value\_type}, \code{reference},
\code{pointer}, \code{difference\_type}, and
\code{iterator\_category}. In the example above, the last two would be
delegated to the \code{Base} iterator type. For many iterator
adaptors, all five must be delegated.
\subsection{Iterator Complexities} \subsection{Iterator Complexities}
In addition to the tedious aspects of iterator implementation, there In addition to the tedious aspects of iterator implementation, there
@ -191,13 +220,17 @@ programmers.
\subsubsection{Constant/Mutable Iterator Interactions} \subsubsection{Constant/Mutable Iterator Interactions}
One of the main situations in which iterators are used is inside Iterators over containers and other sequences of stored objects
containers. These iterators usually come in pairs: a constant iterator usually come in pairs: a constant iterator type and a mutable iterator
type and a mutable iterator type. It is desirable to allow the type. It is desirable to allow the constant and mutable iterators to
constant and mutable iterators to interoperate in terms of their interoperate through comparison and
binary operators. For example, suppose that you are implementing a subtraction. For example, suppose
container type \code{C}. Then you ought to define the following four that you are implementing a container type \code{C}. Then you ought to
version of \code{operator==}. define the following four version of \code{operator==}, along with
corresponding versions of \code{operator!=}, and (for
RandomAccessIterators), operators \code{<}, \code{>},
\code{<=}, \code{>=}, and \code{-}.
{\footnotesize {\footnotesize
\begin{verbatim} \begin{verbatim}
@ -212,24 +245,26 @@ Implementers often forget to define the operators for constant/mutable
iterator interaction. In addition, iterator adaptors applied to these iterator interaction. In addition, iterator adaptors applied to these
kinds of iterators should propagate the ability to interact. For kinds of iterators should propagate the ability to interact. For
example, a reverse iterator adaptor applied to \code{C::iterator} and example, a reverse iterator adaptor applied to \code{C::iterator} and
\code{C::const\_iterator} should result in reverse iterator types that \code{C::const\_iterator} should result in mutable and constant reverse iterator types that
also have operators defined for the constant/mutable interactions. have the same ability to interact as the \code{Base} iterators do.
\subsubsection{Constant/Mutable Iterator Implementation} \subsubsection{Constant/Mutable Iterator Implementation}
% Is this section really worthwhile? It was confusing to read until I
% finally figured out what you were driving at. Who makes this conceptual mistake?
Another subtlety in the implementation of iterators is how the the Another subtlety in the implementation of iterators is how the the
distinction between constant and mutable iterators affects the distinction between constant and mutable iterators affects the
implementation. It is obvious that a constant iterator should have a implementation. It is obvious that a constant iterator should have
const \code{reference} type, while a mutable iterator should have a \code{const} \code{reference} and \code{pointer} types, while a mutable iterator should have a
non-const \code{reference}, though in other regards the constant and non-\code{const} \code{reference} and \code{pointer}, though in other regards the constant and
mutable versions of an iterator are very similar. It is therefore mutable versions of an iterator are the same. It is therefore
desirable to implement both versions of the iterator with a single desirable to implement both versions of the iterator with a the same
class. It is possible to do this, however some care must be taken. code. It is possible to do this, however some care must be taken.
One common mistake is that the programmer will confuse the difference
between a const iterator object and a constant iterator. Such a One common mistake is that the programmer will confuse the ideas of
misunderstanding can, for example, lead to an iterator class that has a \code{const} iterator object and a \emph{constant iterator}. Such a
two versions of \code{operator*}, one that is a const member function misunderstanding can, for example, lead to a single iterator class that has
two versions of \code{operator*}, one that is a \code{const} member function
and one that is not. and one that is not.
{\footnotesize {\footnotesize
@ -241,14 +276,14 @@ and one that is not.
} }
The right way to implement both a constant and mutable iterators using The right way to implement both a constant and mutable iterators using
the same class is to make the iterator a class template and make the the same code is to use a class template parameterized on the
reference type a parameter. To create the constant iterator a const \code{reference} type. To create the constant iterator a \code{const}
reference would be used as the template argument and to create the reference would be used as the template argument and to create the
mutable iterator a non-const reference would be used. There should be mutable iterator a non-\code{const} reference would be used instead.
only one \code{operator*} that returns the \code{reference} type and There should be only one \code{operator*} that returns the
the member function should be const since dereferencing an iterator \code{reference} type and the member function should be \code{const} since
does not change the state of the iterator object itself (unlike dereferencing an iterator does not change the state of the iterator
\code{operator++}). object itself (unlike \code{operator++}).
{\footnotesize {\footnotesize
\begin{verbatim} \begin{verbatim}
@ -260,11 +295,64 @@ does not change the state of the iterator object itself (unlike
\subsubsection{Input Iterators and \code{operator->}} \subsubsection{Input Iterators and \code{operator->}}
When creating an iterator adaptor that can accept an When creating an iterator adaptor that produces an
\stlconcept{InputIterator} as the adapted type some extra care must be \stlconcept{InputIterator} some extra care must be
taken in the implementation of \code{operator->}. \Note{Dave fills in taken in the implementation of \code{operator->}. Remember that an
the rest} input iterator need not iterate over stored objects: it
can manufacture new objects when it is dereferenced as is the case for
\code{std::istream\_iterator}. If the iterator's \code{value\_type} is
of class type, we need to support \code{operator->}. Since the result
of using \code{operator->} must produce a true pointer even when
dereferencing the iterator does not yeild a true reference type, we
need a \code{const} lvalue to which a pointer can be formed.
Fortunately, the standard gives us a way: section 13.3.1.2 paragraph 8
describes a seemingly quirky rule that the \code{->} operator will be
applied to the \emph{result} of any call to \code{operator->}. This is
a convenient way to describe the semantics of ordinary
\code{operator->}, which returns a pointer: it just uses the pointer
to perform the usual member dereferencing. It also turns out to be
what we need to make conforming \stlconcept{InputIterators}. By making
the return type of \code{operator->} a proxy containing an instance of
the iterator's \code{value\_type}, we can eventually form a
\code{const} pointer to the returned temporary:
{\footnotesize
\begin{verbatim}
template <class T>
struct operator_arrow_proxy
{
operator_arrow_proxy(const T& x) : m_value(x) {}
const T* operator->() const { return &m_value; }
T m_value;
};
\end{verbatim}
}
The iterator adaptor library uses a small meta-program to select the
appropriate type for the result of an iterator's \code{operator->}:
{\footnotesize
\begin{verbatim}
template <class Category, class Value, class Pointer>
struct operator_arrow_result_generator
{
// Is it an input iterator, or something more?
static bool const is_input_iter
= is_convertible<Category*,std::input_iterator_tag*>::value
&& !is_convertible<Category*,std::forward_iterator_tag*>::value;
typedef typename type_if<is_input_iter,
operator_arrow_proxy<Value>, /*else*/ Pointer>::type type;
};
\end{verbatim}
}
The boost Type Traits library is used to check whether the iterator's
category is no more refined than \stlconcept{InputIterator}. If so,
the appropriate \code{operator\_arrow\_proxy} is selected.
Convertibility is used as a criterion to allow for user-defined
iterator categories derived from the standard ones.
\subsubsection{The Return Type of \code{operator[]} for Adaptors} \subsubsection{The Return Type of \code{operator[]} for Adaptors}