some minor edits

[SVN r10331]
This commit is contained in:
Jeremy Siek
2001-06-14 16:38:28 +00:00
parent c59a5e8783
commit 2f9bd13902

View File

@ -79,10 +79,10 @@ 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 (adapted iterators). The Iterator variations on other iterators (adapted iterators). The Iterator
Adaptor Library employs template meta-programming and is an example of Adaptor Library is an example of policy-based design and employs
policy-based design. It uses an extremely flexible implementation template meta-programming. It uses an extremely flexible
pattern which can be easily adapted to generate new representatives of implementation pattern which can be easily adapted to generate new
other abstract Concept families. representatives of other abstract Concept families.
\end{abstract} \end{abstract}
@ -95,9 +95,9 @@ other abstract Concept families.
Iterators play an important role in modern C++ programing. The Iterators play an important role in modern C++ programing. The
iterator is the central abstraction of the algorithms of the Standard iterator is the central abstraction of the algorithms of the Standard
Library and creating new iterator types and adapting old ones are Library and creating new iterator types is a common task for C++
common tasks for C++ programmers. There are plenty of examples of programmers. There are plenty of examples of iterators in the
iterators in the literature: 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{std::istream\_iterator} and \code{std::istream\_iterator} and
@ -229,7 +229,7 @@ type. It is desirable to allow the constant and mutable iterators to
interoperate through comparison and interoperate through comparison and
subtraction. For example, suppose subtraction. For example, suppose
that you are implementing a container type \code{C}. Then you ought to that you are implementing a container type \code{C}. Then you ought to
define the following four version of \code{operator==}, along with define the following four versions of \code{operator==}, along with
corresponding versions of \code{operator!=}, and (for corresponding versions of \code{operator!=}, and (for
\stlconcept{RandomAccessIterator}), operators \code{<}, \code{>}, \stlconcept{RandomAccessIterator}), operators \code{<}, \code{>},
\code{<=}, \code{>=}, and \code{-}. \code{<=}, \code{>=}, and \code{-}.
@ -344,7 +344,7 @@ the primary communication mechanism between the iterator implementer
and the \code{iterator\_\-adaptor}; it specifies how the new iterator and the \code{iterator\_\-adaptor}; it specifies how the new iterator
type is different from the \code{Base} type. Unlike the policy classes type is different from the \code{Base} type. Unlike the policy classes
in~\cite{alexandrescu01:_modern_cpp_design}, we group several policies in~\cite{alexandrescu01:_modern_cpp_design}, we group several policies
into a single class, as this proved more convenient for iterator into a single class as this proved more convenient for iterator
implementation. implementation.
\subsection{Iterator Policies Class} \subsection{Iterator Policies Class}
@ -523,7 +523,7 @@ following constructor.
} }
It would be cumbersome for the user to call this constructor since It would be cumbersome for the user to call this constructor since
they would have to separately construct a policies class object and they would have to separately construct a policies object and
then the iterator object. We therefore recommend that iterator then the iterator object. We therefore recommend that iterator
implementers create an \emph{object generator} function for their implementers create an \emph{object generator} function for their
iterator. The following is the generator function for the transform iterator. The following is the generator function for the transform
@ -620,7 +620,7 @@ namespace boost {
// the policies are ``second''. // the policies are ``second''.
Policies& policies() { return m_iter_p.second(); } Policies& policies() { return m_iter_p.second(); }
Base& iter() { return m_iter_p.first(); } Base& iter() { return m_iter_p.first(); }
// and the same for const references... // and similarly for const...
}; };
// Core binary operators. // Core binary operators.
// Redundant binary operators. // Redundant binary operators.
@ -658,13 +658,11 @@ default was used for \code{value\_type} then the \code{reference} and
\subsubsection{Named Template Parameters} \subsubsection{Named Template Parameters}
\label{sec:named-template-parameters} \label{sec:named-template-parameters}
The \code{iterator\_adaptor} class has five template parameters that Since each of the parameters for the associated types has a default
specify the associated types of the iterator. Each of the parameters user may specify zero or more of them. One difficulty with {C++}
has defaults as described above, so the user may specify zero or more templates is that if a default is used for a parameter then all the
parameters. One difficulty with {C++} templates is that if a default following parameters must also be default. When there are a large
is used for a parameter then all the following parameters must also be number of parameters this becomes inconvenient.
default. When there are a large number of parameters this becomes
inconvenient.
A solution to this problem is the idea of named parameters. Instead A solution to this problem is the idea of named parameters. Instead
of matching arguments to parameters based on order, the assignment of of matching arguments to parameters based on order, the assignment of
@ -682,13 +680,13 @@ example:
\end{verbatim} \end{verbatim}
} }
Instead of passing the argument for the \code{Value} type directly to Instead of passing the argument \code{Value} directly to
\code{iterator\_\-adaptor} the user passes \code{iterator\_\-adaptor} the user passes
\code{value\_type\_is<Value>}. The \code{iterator\_\-adaptor} now has \code{value\_type\_is<Value>}. The \code{iterator\_\-adaptor} has
five arguments for the associated types, each of which could be used five arguments for the associated types, each of which could be used
to specify any of the actual parameters. The to specify any of the actual parameters. The
\code{iterator\_\-adaptor} must deduce which argument is for which \code{iterator\_\-adaptor} must deduce which argument is for which
parameter based on the \code{tag} type inside the wrapper. parameter based on the \code{tag} inside the wrapper.
First we take all of the parameters and place them in a First we take all of the parameters and place them in a
lisp-style list, using \code{std::pair} for \code{cons}. Each lisp-style list, using \code{std::pair} for \code{cons}. Each
@ -806,31 +804,37 @@ in the following two sections.
\subsubsection{Implementing \code{operator->} for Input Iterators} \subsubsection{Implementing \code{operator->} for Input Iterators}
\label{sec:operator-arrow} \label{sec:operator-arrow}
Fortunately, the standard gives us a way: section 13.3.1.2 paragraph 8 As introduced in \S\ref{sec:operator-arrow}, it is difficult to
describes a seemingly quirky rule that the \code{->} operator will be implement \code{operator->} for input iterators because there may not
applied to the \emph{result} of any call to \code{operator->}. This is be an lvalue from which to form a pointer.
a convenient way to describe the semantics of ordinary
\code{operator->}, which returns a pointer: it just uses the pointer Fortunately, the standard makes a workaround possible: section
to perform the usual member dereferencing. It also turns out to be 13.3.1.2 paragraph 8 describes a seemingly quirky rule that the
what we need to make a conforming \stlconcept{InputIterator}. By \code{->} operator will be applied to the \emph{result} of any call to
making the return type of \code{operator->} a proxy containing an \code{operator->}. This is a convenient way to describe the semantics
instance of the iterator's \code{value\_type}, we can eventually form of ordinary \code{operator->}, which returns a pointer: it just uses
a \code{const} pointer to the returned temporary: the pointer to perform the usual member dereferencing. It also turns
out to be what we need to make a conforming
\stlconcept{InputIterator}. 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 {\footnotesize
\begin{verbatim} \begin{verbatim}
template <class T> template <class T>
struct operator_arrow_proxy struct operator_arrow_proxy
{ {
operator_arrow_proxy(const T& x) : m_value(x) {} operator_arrow_proxy(const T& x) : m_value(x) {}
const T* operator->() const { return &m_value; } const T* operator->() const { return &m_value; }
T m_value; T m_value;
}; };
\end{verbatim} \end{verbatim}
} }
The iterator adaptor library uses a small meta-program to select the \noindent The iterator adaptor library uses a small meta-program to
appropriate type for the result of an iterator's \code{operator->}: select the appropriate type for the result of an iterator's
\code{operator->}:
{\footnotesize {\footnotesize
\begin{verbatim} \begin{verbatim}
@ -843,7 +847,7 @@ appropriate type for the result of an iterator's \code{operator->}:
&& !is_convertible<Category*,std::forward_iterator_tag*>::value; && !is_convertible<Category*,std::forward_iterator_tag*>::value;
typedef typename type_if<is_input_iter, typedef typename type_if<is_input_iter,
operator_arrow_proxy<Value>, /*else*/ Pointer>::type type; operator_arrow_proxy<Value>, /*else*/ Pointer>::type type;
}; };
\end{verbatim} \end{verbatim}
} }
@ -856,10 +860,11 @@ iterator categories derived from the standard ones.
\subsubsection{Implementation of \code{operator[]}} \subsubsection{Implementation of \code{operator[]}}
\label{sec:op-bracket-impl}
The implementation of \code{operator[]} would be trivial except for The implementation of \code{operator[]} would be trivial except for
the issue surrounding what the return type should be. As mentioned in the issue surrounding what the return type should be. As mentioned in
\S\ref{sec:iterator-bracket}, it would be dangerous to make the \S\ref{sec:operator-bracket}, it would be dangerous to make the
\code{iterator\_adaptor} always return a reference for \code{iterator\_adaptor} always return a reference for
\code{operator[]} for there are certain situations in which this can \code{operator[]} for there are certain situations in which this can
cause run-time errors. cause run-time errors.
@ -937,14 +942,14 @@ of course that will take some time to gain acceptance.
Constructing iterators and iterator adaptors is a common task for Constructing iterators and iterator adaptors is a common task for
modern C++ programming. Despite the conceptual simplicity of most modern C++ programming. Despite the conceptual simplicity of most
iterators, implementing {C++} Standard conforming iterators requires iterators, implementing {C++} Standard conforming iterators requires a
is non-trivial amount of code: some of which is challenging to get non-trivial amount of code: some of which is challenging to get right
right, and a lot of which is tedious to write. The and a lot of which is tedious. The \code{iterator\_adaptor} class that
\code{iterator\_adaptor} class that we present in this paper solves we present in this paper solves this problem by providing a mechanism
this problem by providing a mechanism by which the user provides a by which the user provides a minimal specification (by way of the
minimal specification (by way of the policies class) for the iterator, policies class) for the iterator, and then the
and then the \code{iterator\_\-adaptor} takes care of most of the \code{iterator\_\-adaptor} takes care of most of the implementation
implementation details. details.
Taking a step back, the design approach was to create a canonical Taking a step back, the design approach was to create a canonical
implementation of a concept (iterator) and then delegate the core implementation of a concept (iterator) and then delegate the core