mirror of
				https://github.com/boostorg/utility.git
				synced 2025-11-04 02:11:45 +01:00 
			
		
		
		
	Compare commits
	
		
			22 Commits
		
	
	
		
			svn-branch
			...
			svn-branch
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					e608036e99 | ||
| 
						 | 
					91c135c0af | ||
| 
						 | 
					98e87c8afb | ||
| 
						 | 
					d9e0f80d50 | ||
| 
						 | 
					6396fdb5ff | ||
| 
						 | 
					2470b53373 | ||
| 
						 | 
					16334e92ca | ||
| 
						 | 
					c22d98a8ec | ||
| 
						 | 
					28617afbb9 | ||
| 
						 | 
					0c3bc42bec | ||
| 
						 | 
					e3d9745df1 | ||
| 
						 | 
					b8471c1015 | ||
| 
						 | 
					045b09c9ef | ||
| 
						 | 
					4ac07b97d3 | ||
| 
						 | 
					34c847c17f | ||
| 
						 | 
					f694e557e1 | ||
| 
						 | 
					6a0c3e92a0 | ||
| 
						 | 
					cba48df8e3 | ||
| 
						 | 
					a0e8d1bf36 | ||
| 
						 | 
					912dedaca7 | ||
| 
						 | 
					7dd90c3919 | ||
| 
						 | 
					7c3a25a377 | 
@@ -1,489 +0,0 @@
 | 
			
		||||
<html>
 | 
			
		||||
 | 
			
		||||
<head>
 | 
			
		||||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
 | 
			
		||||
<meta name="GENERATOR" content="Microsoft FrontPage 4.0">
 | 
			
		||||
<meta name="ProgId" content="FrontPage.Editor.Document">
 | 
			
		||||
<title>C++ Type traits</title>
 | 
			
		||||
</head>
 | 
			
		||||
 | 
			
		||||
<body bgcolor="#FFFFFF" link="#0000FF" vlink="#800080">
 | 
			
		||||
 | 
			
		||||
<h2 align="center">C++ Type traits</h2>
 | 
			
		||||
<p align="center"><em>by John Maddock and Steve Cleary</em></p>
 | 
			
		||||
<p align="center"><em>This is a draft of an article that will appear in a future
 | 
			
		||||
issue of </em><a href="http://www.ddj.com"><em>Dr Dobb's Journal</em></a></p>
 | 
			
		||||
<p>Generic programming (writing code which works with any data type meeting a
 | 
			
		||||
set of requirements) has become the method of choice for providing reusable
 | 
			
		||||
code. However, there are times in generic programming when "generic"
 | 
			
		||||
just isn't good enough - sometimes the differences between types are too large
 | 
			
		||||
for an efficient generic implementation. This is when the traits technique
 | 
			
		||||
becomes important - by encapsulating those properties that need to be considered
 | 
			
		||||
on a type by type basis inside a traits class, we can minimise the amount of
 | 
			
		||||
code that has to differ from one type to another, and maximise the amount of
 | 
			
		||||
generic code.</p>
 | 
			
		||||
<p>Consider an example: when working with character strings, one common
 | 
			
		||||
operation is to determine the length of a null terminated string. Clearly it's
 | 
			
		||||
possible to write generic code that can do this, but it turns out that there are
 | 
			
		||||
much more efficient methods available: for example, the C library functions <font size="2" face="Courier New">strlen</font>
 | 
			
		||||
and <font size="2" face="Courier New">wcslen</font> are usually written in
 | 
			
		||||
assembler, and with suitable hardware support can be considerably faster than a
 | 
			
		||||
generic version written in C++. The authors of the C++ standard library realised
 | 
			
		||||
this, and abstracted the properties of <font size="2" face="Courier New">char</font>
 | 
			
		||||
and <font size="2" face="Courier New">wchar_t</font> into the class <font size="2" face="Courier New">char_traits</font>.
 | 
			
		||||
Generic code that works with character strings can simply use <font size="2" face="Courier New">char_traits<>::length</font>
 | 
			
		||||
to determine the length of a null terminated string, safe in the knowledge that
 | 
			
		||||
specialisations of <font size="2" face="Courier New">char_traits</font> will use
 | 
			
		||||
the most appropriate method available to them.</p>
 | 
			
		||||
<h4>Type traits</h4>
 | 
			
		||||
<p>Class <font size="2" face="Courier New">char_traits</font> is a classic
 | 
			
		||||
example of a collection of type specific properties wrapped up in a single class
 | 
			
		||||
- what Nathan Myers termed a <i>baggage class</i>[1]. In the Boost type-traits
 | 
			
		||||
library, we[2] have written a set of very specific traits classes, each of which
 | 
			
		||||
encapsulate a single trait from the C++ type system; for example, is a type a
 | 
			
		||||
pointer or a reference type? Or does a type have a trivial constructor, or a
 | 
			
		||||
const-qualifier? The type-traits classes share a unified design: each class has
 | 
			
		||||
a single member <i>value</i>, a compile-time constant that is true if the type
 | 
			
		||||
has the specified property, and false otherwise. As we will show, these classes
 | 
			
		||||
can be used in generic programming to determine the properties of a given type
 | 
			
		||||
and introduce optimisations that are appropriate for that case.</p>
 | 
			
		||||
<p>The type-traits library also contains a set of classes that perform a
 | 
			
		||||
specific transformation on a type; for example, they can remove a top-level
 | 
			
		||||
const or volatile qualifier from a type. Each class that performs a
 | 
			
		||||
transformation defines a single typedef-member <i>type</i> that is the result of
 | 
			
		||||
the transformation. All of the type-traits classes are defined inside namespace <font size="2" face="Courier New">boost</font>;
 | 
			
		||||
for brevity, namespace-qualification is omitted in most of the code samples
 | 
			
		||||
given.</p>
 | 
			
		||||
<h4>Implementation</h4>
 | 
			
		||||
<p>There are far too many separate classes contained in the type-traits library
 | 
			
		||||
to give a full implementation here - see the source code in the Boost library
 | 
			
		||||
for the full details - however, most of the implementation is fairly repetitive
 | 
			
		||||
anyway, so here we will just give you a flavour for how some of the classes are
 | 
			
		||||
implemented. Beginning with possibly the simplest class in the library, is_void<T>
 | 
			
		||||
has a member <i>value</i> that is true only if T is void.</p>
 | 
			
		||||
<pre>template <typename T> 
 | 
			
		||||
struct is_void
 | 
			
		||||
{ static const bool value = false; };
 | 
			
		||||
 | 
			
		||||
template <> 
 | 
			
		||||
struct is_void<void>
 | 
			
		||||
{ static const bool value = true; };</pre>
 | 
			
		||||
<p>Here we define a primary version of the template class <font size="2" face="Courier New">is_void</font>,
 | 
			
		||||
and provide a full-specialisation when T is void. While full specialisation of a
 | 
			
		||||
template class is an important technique, sometimes we need a solution that is
 | 
			
		||||
halfway between a fully generic solution, and a full specialisation. This is
 | 
			
		||||
exactly the situation for which the standards committee defined partial
 | 
			
		||||
template-class specialisation. As an example, consider the class
 | 
			
		||||
boost::is_pointer<T>: here we needed a primary version that handles all
 | 
			
		||||
the cases where T is not a pointer, and a partial specialisation to handle all
 | 
			
		||||
the cases where T is a pointer:</p>
 | 
			
		||||
<pre>template <typename T> 
 | 
			
		||||
struct is_pointer 
 | 
			
		||||
{ static const bool value = false; };
 | 
			
		||||
 | 
			
		||||
template <typename T> 
 | 
			
		||||
struct is_pointer<T*> 
 | 
			
		||||
{ static const bool value = true; };</pre>
 | 
			
		||||
<p>The syntax for partial specialisation is somewhat arcane and could easily
 | 
			
		||||
occupy an article in its own right; like full specialisation, in order to write
 | 
			
		||||
a partial specialisation for a class, you must first declare the primary
 | 
			
		||||
template. The partial specialisation contains an extra <<EFBFBD>> after the
 | 
			
		||||
class name that contains the partial specialisation parameters; these define the
 | 
			
		||||
types that will bind to that partial specialisation rather than the default
 | 
			
		||||
template. The rules for what can appear in a partial specialisation are somewhat
 | 
			
		||||
convoluted, but as a rule of thumb if you can legally write two function
 | 
			
		||||
overloads of the form:</p>
 | 
			
		||||
<pre>void foo(T);
 | 
			
		||||
void foo(U);</pre>
 | 
			
		||||
<p>Then you can also write a partial specialisation of the form:</p>
 | 
			
		||||
<pre>template <typename T>
 | 
			
		||||
class c{ /*details*/ };
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
 | 
			
		||||
class c<U>{ /*details*/ };</pre>
 | 
			
		||||
<p>This rule is by no means foolproof, but it is reasonably simple to remember
 | 
			
		||||
and close enough to the actual rule to be useful for everyday use.</p>
 | 
			
		||||
<p>As a more complex example of partial specialisation consider the class
 | 
			
		||||
remove_bounds<T>. This class defines a single typedef-member <i>type</i>
 | 
			
		||||
that is the same type as T but with any top-level array bounds removed; this is
 | 
			
		||||
an example of a traits class that performs a transformation on a type:</p>
 | 
			
		||||
<pre>template <typename T> 
 | 
			
		||||
struct remove_bounds
 | 
			
		||||
{ typedef T type; };
 | 
			
		||||
 | 
			
		||||
template <typename T, std::size_t N> 
 | 
			
		||||
struct remove_bounds<T[N]>
 | 
			
		||||
{ typedef T type; };</pre>
 | 
			
		||||
<p>The aim of remove_bounds is this: imagine a generic algorithm that is passed
 | 
			
		||||
an array type as a template parameter, <font size="2" face="Courier New">remove_bounds</font>
 | 
			
		||||
provides a means of determining the underlying type of the array. For example <code>remove_bounds<int[4][5]>::type</code>
 | 
			
		||||
would evaluate to the type <code>int[5]</code>. This example also shows that the
 | 
			
		||||
number of template parameters in a partial specialisation does not have to match
 | 
			
		||||
the number in the default template. However, the number of parameters that
 | 
			
		||||
appear after the class name do have to match the number and type of the
 | 
			
		||||
parameters in the default template.</p>
 | 
			
		||||
<h4>Optimised copy</h4>
 | 
			
		||||
<p>As an example of how the type traits classes can be used, consider the
 | 
			
		||||
standard library algorithm copy:</p>
 | 
			
		||||
<pre>template<typename Iter1, typename Iter2>
 | 
			
		||||
Iter2 copy(Iter1 first, Iter1 last, Iter2 out);</pre>
 | 
			
		||||
<p>Obviously, there's no problem writing a generic version of copy that works
 | 
			
		||||
for all iterator types Iter1 and Iter2; however, there are some circumstances
 | 
			
		||||
when the copy operation can best be performed by a call to <font size="2" face="Courier New">memcpy</font>.
 | 
			
		||||
In order to implement copy in terms of <font size="2" face="Courier New">memcpy</font>
 | 
			
		||||
all of the following conditions need to be met:</p>
 | 
			
		||||
<ul>
 | 
			
		||||
  <li>Both of the iterator types Iter1 and Iter2 must be pointers.</li>
 | 
			
		||||
  <li>Both Iter1 and Iter2 must point to the same type - excluding <font size="2" face="Courier New">const</font>
 | 
			
		||||
    and <font size="2" face="Courier New">volatile</font>-qualifiers.</li>
 | 
			
		||||
  <li>The type pointed to by Iter1 must have a trivial assignment operator.</li>
 | 
			
		||||
</ul>
 | 
			
		||||
<p>By trivial assignment operator we mean that the type is either a scalar
 | 
			
		||||
type[3] or:</p>
 | 
			
		||||
<ul>
 | 
			
		||||
  <li>The type has no user defined assignment operator.</li>
 | 
			
		||||
  <li>The type does not have any data members that are references.</li>
 | 
			
		||||
  <li>All base classes, and all data member objects must have trivial assignment
 | 
			
		||||
    operators.</li>
 | 
			
		||||
</ul>
 | 
			
		||||
<p>If all these conditions are met then a type can be copied using <font size="2" face="Courier New">memcpy</font>
 | 
			
		||||
rather than using a compiler generated assignment operator. The type-traits
 | 
			
		||||
library provides a class <i>has_trivial_assign</i>, such that <code>has_trivial_assign<T>::value</code>
 | 
			
		||||
is true only if T has a trivial assignment operator. This class "just
 | 
			
		||||
works" for scalar types, but has to be explicitly specialised for
 | 
			
		||||
class/struct types that also happen to have a trivial assignment operator. In
 | 
			
		||||
other words if <i>has_trivial_assign</i> gives the wrong answer, it will give
 | 
			
		||||
the "safe" wrong answer - that trivial assignment is not allowable.</p>
 | 
			
		||||
<p>The code for an optimised version of copy that uses <font size="2" face="Courier New">memcpy</font>
 | 
			
		||||
where appropriate is given in listing 1. The code begins by defining a template
 | 
			
		||||
class <i>copier</i>, that takes a single Boolean template parameter, and has a
 | 
			
		||||
static template member function <font size="2" face="Courier New">do_copy</font>
 | 
			
		||||
which performs the generic version of <font size="2">copy</font> (in other words
 | 
			
		||||
the "slow but safe version"). Following that there is a specialisation
 | 
			
		||||
for <i>copier<true></i>: again this defines a static template member
 | 
			
		||||
function <font size="2" face="Courier New">do_copy</font>, but this version uses
 | 
			
		||||
memcpy to perform an "optimised" copy.</p>
 | 
			
		||||
<p>In order to complete the implementation, what we need now is a version of
 | 
			
		||||
copy, that calls <code>copier<true>::do_copy</code> if it is safe to use <font size="2" face="Courier New">memcpy</font>,
 | 
			
		||||
and otherwise calls <code>copier<false>::do_copy</code> to do a
 | 
			
		||||
"generic" copy. This is what the version in listing 1 does. To
 | 
			
		||||
understand how the code works look at the code for <font size="2" face="Courier New">copy</font>
 | 
			
		||||
and consider first the two typedefs <i>v1_t</i> and <i>v2_t</i>. These use <code>std::iterator_traits<Iter1>::value_type</code>
 | 
			
		||||
to determine what type the two iterators point to, and then feed the result into
 | 
			
		||||
another type-traits class <i>remove_cv</i> that removes the top-level
 | 
			
		||||
const-volatile-qualifiers: this will allow copy to compare the two types without
 | 
			
		||||
regard to const- or volatile-qualifiers. Next, <font size="2" face="Courier New">copy</font>
 | 
			
		||||
declares an enumerated value <i>can_opt</i> that will become the template
 | 
			
		||||
parameter to copier - declaring this here as a constant is really just a
 | 
			
		||||
convenience - the value could be passed directly to class <font size="2" face="Courier New">copier</font>.
 | 
			
		||||
The value of <i>can_opt</i> is computed by verifying that all of the following
 | 
			
		||||
are true:</p>
 | 
			
		||||
<ul>
 | 
			
		||||
  <li>first that the two iterators point to the same type by using a type-traits
 | 
			
		||||
    class <i>is_same</i>.</li>
 | 
			
		||||
  <li>Then that both iterators are real pointers - using the class <i>is_pointer</i>
 | 
			
		||||
    described above.</li>
 | 
			
		||||
  <li>Finally that the pointed-to types have a trivial assignment operator using
 | 
			
		||||
    <i>has_trivial_assign</i>.</li>
 | 
			
		||||
</ul>
 | 
			
		||||
<p>Finally we can use the value of <i>can_opt</i> as the template argument to
 | 
			
		||||
copier - this version of copy will now adapt to whatever parameters are passed
 | 
			
		||||
to it, if its possible to use <font size="2" face="Courier New">memcpy</font>,
 | 
			
		||||
then it will do so, otherwise it will use a generic copy.</p>
 | 
			
		||||
<h4>Was it worth it?</h4>
 | 
			
		||||
<p>It has often been repeated in these columns that "premature optimisation
 | 
			
		||||
is the root of all evil" [4]. So the question must be asked: was our
 | 
			
		||||
optimisation premature? To put this in perspective the timings for our version
 | 
			
		||||
of copy compared a conventional generic copy[5] are shown in table 1.</p>
 | 
			
		||||
<p>Clearly the optimisation makes a difference in this case; but, to be fair,
 | 
			
		||||
the timings are loaded to exclude cache miss effects - without this accurate
 | 
			
		||||
comparison between algorithms becomes difficult. However, perhaps we can add a
 | 
			
		||||
couple of caveats to the premature optimisation rule:</p>
 | 
			
		||||
<ul>
 | 
			
		||||
  <li>If you use the right algorithm for the job in the first place then
 | 
			
		||||
    optimisation will not be required; in some cases, <font size="2" face="Courier New">memcpy</font>
 | 
			
		||||
    is the right algorithm.</li>
 | 
			
		||||
  <li>If a component is going to be reused in many places by many people then
 | 
			
		||||
    optimisations may well be worthwhile where they would not be so for a single
 | 
			
		||||
    case - in other words, the likelihood that the optimisation will be
 | 
			
		||||
    absolutely necessary somewhere, sometime is that much higher. Just as
 | 
			
		||||
    importantly the perceived value of the stock implementation will be higher:
 | 
			
		||||
    there is no point standardising an algorithm if users reject it on the
 | 
			
		||||
    grounds that there are better, more heavily optimised versions available.</li>
 | 
			
		||||
</ul>
 | 
			
		||||
<h4>Table 1: Time taken to copy 1000 elements using copy<const T*, T*>
 | 
			
		||||
(times in micro-seconds)</h4>
 | 
			
		||||
<table border="1" cellpadding="7" cellspacing="1" width="529">
 | 
			
		||||
  <tr>
 | 
			
		||||
    <td valign="top" width="33%">
 | 
			
		||||
      <p align="center">Version</p>
 | 
			
		||||
    </td>
 | 
			
		||||
    <td valign="top" width="33%">
 | 
			
		||||
      <p align="center">T</p>
 | 
			
		||||
    </td>
 | 
			
		||||
    <td valign="top" width="33%">
 | 
			
		||||
      <p align="center">Time</p>
 | 
			
		||||
    </td>
 | 
			
		||||
  </tr>
 | 
			
		||||
  <tr>
 | 
			
		||||
    <td valign="top" width="33%">"Optimised" copy</td>
 | 
			
		||||
    <td valign="top" width="33%">char</td>
 | 
			
		||||
    <td valign="top" width="33%">0.99</td>
 | 
			
		||||
  </tr>
 | 
			
		||||
  <tr>
 | 
			
		||||
    <td valign="top" width="33%">Conventional copy</td>
 | 
			
		||||
    <td valign="top" width="33%">char</td>
 | 
			
		||||
    <td valign="top" width="33%">8.07</td>
 | 
			
		||||
  </tr>
 | 
			
		||||
  <tr>
 | 
			
		||||
    <td valign="top" width="33%">"Optimised" copy</td>
 | 
			
		||||
    <td valign="top" width="33%">int</td>
 | 
			
		||||
    <td valign="top" width="33%">2.52</td>
 | 
			
		||||
  </tr>
 | 
			
		||||
  <tr>
 | 
			
		||||
    <td valign="top" width="33%">Conventional copy</td>
 | 
			
		||||
    <td valign="top" width="33%">int</td>
 | 
			
		||||
    <td valign="top" width="33%">8.02</td>
 | 
			
		||||
  </tr>
 | 
			
		||||
</table>
 | 
			
		||||
<p> </p>
 | 
			
		||||
<h4>Pair of References</h4>
 | 
			
		||||
<p>The optimised copy example shows how type traits may be used to perform
 | 
			
		||||
optimisation decisions at compile-time. Another important usage of type traits
 | 
			
		||||
is to allow code to compile that otherwise would not do so unless excessive
 | 
			
		||||
partial specialization is used. This is possible by delegating partial
 | 
			
		||||
specialization to the type traits classes. Our example for this form of usage is
 | 
			
		||||
a pair that can hold references [6].</p>
 | 
			
		||||
<p>First, let us examine the definition of "std::pair", omitting the
 | 
			
		||||
comparision operators, default constructor, and template copy constructor for
 | 
			
		||||
simplicity:</p>
 | 
			
		||||
<pre>template <typename T1, typename T2> 
 | 
			
		||||
struct pair 
 | 
			
		||||
{
 | 
			
		||||
  typedef T1 first_type;
 | 
			
		||||
  typedef T2 second_type;
 | 
			
		||||
 | 
			
		||||
  T1 first;
 | 
			
		||||
  T2 second;
 | 
			
		||||
 | 
			
		||||
  pair(const T1 & nfirst, const T2 & nsecond)
 | 
			
		||||
  :first(nfirst), second(nsecond) { }
 | 
			
		||||
};</pre>
 | 
			
		||||
<p>Now, this "pair" cannot hold references as it currently stands,
 | 
			
		||||
because the constructor would require taking a reference to a reference, which
 | 
			
		||||
is currently illegal [7]. Let us consider what the constructor's parameters
 | 
			
		||||
would have to be in order to allow "pair" to hold non-reference types,
 | 
			
		||||
references, and constant references:</p>
 | 
			
		||||
<table border="1" cellpadding="7" cellspacing="1" width="638">
 | 
			
		||||
  <tr>
 | 
			
		||||
    <td valign="top" width="50%">Type of "T1"</td>
 | 
			
		||||
    <td valign="top" width="50%">Type of parameter to initializing constructor</td>
 | 
			
		||||
  </tr>
 | 
			
		||||
  <tr>
 | 
			
		||||
    <td valign="top" width="50%">
 | 
			
		||||
      <pre>T</pre>
 | 
			
		||||
    </td>
 | 
			
		||||
    <td valign="top" width="50%">
 | 
			
		||||
      <pre>const T &</pre>
 | 
			
		||||
    </td>
 | 
			
		||||
  </tr>
 | 
			
		||||
  <tr>
 | 
			
		||||
    <td valign="top" width="50%">
 | 
			
		||||
      <pre>T &</pre>
 | 
			
		||||
    </td>
 | 
			
		||||
    <td valign="top" width="50%">
 | 
			
		||||
      <pre>T &</pre>
 | 
			
		||||
    </td>
 | 
			
		||||
  </tr>
 | 
			
		||||
  <tr>
 | 
			
		||||
    <td valign="top" width="50%">
 | 
			
		||||
      <pre>const T &</pre>
 | 
			
		||||
    </td>
 | 
			
		||||
    <td valign="top" width="50%">
 | 
			
		||||
      <pre>const T &</pre>
 | 
			
		||||
    </td>
 | 
			
		||||
  </tr>
 | 
			
		||||
</table>
 | 
			
		||||
<p>A little familiarity with the type traits classes allows us to construct a
 | 
			
		||||
single mapping that allows us to determine the type of parameter from the type
 | 
			
		||||
of the contained class. The type traits classes provide a transformation "add_reference",
 | 
			
		||||
which adds a reference to its type, unless it is already a reference.</p>
 | 
			
		||||
<table border="1" cellpadding="7" cellspacing="1" width="580">
 | 
			
		||||
  <tr>
 | 
			
		||||
    <td valign="top" width="21%">Type of "T1"</td>
 | 
			
		||||
    <td valign="top" width="27%">Type of "const T1"</td>
 | 
			
		||||
    <td valign="top" width="53%">Type of "add_reference<const
 | 
			
		||||
      T1>::type"</td>
 | 
			
		||||
  </tr>
 | 
			
		||||
  <tr>
 | 
			
		||||
    <td valign="top" width="21%">
 | 
			
		||||
      <pre>T</pre>
 | 
			
		||||
    </td>
 | 
			
		||||
    <td valign="top" width="27%">
 | 
			
		||||
      <pre>const T</pre>
 | 
			
		||||
    </td>
 | 
			
		||||
    <td valign="top" width="53%">
 | 
			
		||||
      <pre>const T &</pre>
 | 
			
		||||
    </td>
 | 
			
		||||
  </tr>
 | 
			
		||||
  <tr>
 | 
			
		||||
    <td valign="top" width="21%">
 | 
			
		||||
      <pre>T &</pre>
 | 
			
		||||
    </td>
 | 
			
		||||
    <td valign="top" width="27%">
 | 
			
		||||
      <pre>T & [8]</pre>
 | 
			
		||||
    </td>
 | 
			
		||||
    <td valign="top" width="53%">
 | 
			
		||||
      <pre>T &</pre>
 | 
			
		||||
    </td>
 | 
			
		||||
  </tr>
 | 
			
		||||
  <tr>
 | 
			
		||||
    <td valign="top" width="21%">
 | 
			
		||||
      <pre>const T &</pre>
 | 
			
		||||
    </td>
 | 
			
		||||
    <td valign="top" width="27%">
 | 
			
		||||
      <pre>const T &</pre>
 | 
			
		||||
    </td>
 | 
			
		||||
    <td valign="top" width="53%">
 | 
			
		||||
      <pre>const T &</pre>
 | 
			
		||||
    </td>
 | 
			
		||||
  </tr>
 | 
			
		||||
</table>
 | 
			
		||||
<p>This allows us to build a primary template definition for "pair"
 | 
			
		||||
that can contain non-reference types, reference types, and constant reference
 | 
			
		||||
types:</p>
 | 
			
		||||
<pre>template <typename T1, typename T2> 
 | 
			
		||||
struct pair 
 | 
			
		||||
{
 | 
			
		||||
  typedef T1 first_type;
 | 
			
		||||
  typedef T2 second_type;
 | 
			
		||||
 | 
			
		||||
  T1 first;
 | 
			
		||||
  T2 second;
 | 
			
		||||
 | 
			
		||||
  pair(boost::add_reference<const T1>::type nfirst,
 | 
			
		||||
       boost::add_reference<const T2>::type nsecond)
 | 
			
		||||
  :first(nfirst), second(nsecond) { }
 | 
			
		||||
};</pre>
 | 
			
		||||
<p>Add back in the standard comparision operators, default constructor, and
 | 
			
		||||
template copy constructor (which are all the same), and you have a std::pair
 | 
			
		||||
that can hold reference types!</p>
 | 
			
		||||
<p>This same extension <i>could</i> have been done using partial template
 | 
			
		||||
specialization of "pair", but to specialize "pair" in this
 | 
			
		||||
way would require three partial specializations, plus the primary template. Type
 | 
			
		||||
traits allows us to define a single primary template that adjusts itself
 | 
			
		||||
auto-magically to any of these partial specializations, instead of a brute-force
 | 
			
		||||
partial specialization approach. Using type traits in this fashion allows
 | 
			
		||||
programmers to delegate partial specialization to the type traits classes,
 | 
			
		||||
resulting in code that is easier to maintain and easier to understand.</p>
 | 
			
		||||
<h4>Conclusion</h4>
 | 
			
		||||
<p>We hope that in this article we have been able to give you some idea of what
 | 
			
		||||
type-traits are all about. A more complete listing of the available classes are
 | 
			
		||||
in the boost documentation, along with further examples using type traits.
 | 
			
		||||
Templates have enabled C++ uses to take the advantage of the code reuse that
 | 
			
		||||
generic programming brings; hopefully this article has shown that generic
 | 
			
		||||
programming does not have to sink to the lowest common denominator, and that
 | 
			
		||||
templates can be optimal as well as generic.</p>
 | 
			
		||||
<h4>Acknowledgements</h4>
 | 
			
		||||
<p>The authors would like to thank Beman Dawes and Howard Hinnant for their
 | 
			
		||||
helpful comments when preparing this article.</p>
 | 
			
		||||
<h4>References</h4>
 | 
			
		||||
<ol>
 | 
			
		||||
  <li>Nathan C. Myers, C++ Report, June 1995.</li>
 | 
			
		||||
  <li>The type traits library is based upon contributions by Steve Cleary, Beman
 | 
			
		||||
    Dawes, Howard Hinnant and John Maddock: it can be found at www.boost.org.</li>
 | 
			
		||||
  <li>A scalar type is an arithmetic type (i.e. a built-in integer or floating
 | 
			
		||||
    point type), an enumeration type, a pointer, a pointer to member, or a
 | 
			
		||||
    const- or volatile-qualified version of one of these types.</li>
 | 
			
		||||
  <li>This quote is from Donald Knuth, ACM Computing Surveys, December 1974, pg
 | 
			
		||||
    268.</li>
 | 
			
		||||
  <li>The test code is available as part of the boost utility library (see
 | 
			
		||||
    algo_opt_examples.cpp), the code was compiled with gcc 2.95 with all
 | 
			
		||||
    optimisations turned on, tests were conducted on a 400MHz Pentium II machine
 | 
			
		||||
    running Microsoft Windows 98.</li>
 | 
			
		||||
  <li>John Maddock and Howard Hinnant have submitted a "compressed_pair"
 | 
			
		||||
    library to Boost, which uses a technique similar to the one described here
 | 
			
		||||
    to hold references. Their pair also uses type traits to determine if any of
 | 
			
		||||
    the types are empty, and will derive instead of contain to conserve space --
 | 
			
		||||
    hence the name "compressed".</li>
 | 
			
		||||
  <li>This is actually an issue with the C++ Core Language Working Group (issue
 | 
			
		||||
    #106), submitted by Bjarne Stroustrup. The tentative resolution is to allow
 | 
			
		||||
    a "reference to a reference to T" to mean the same thing as a
 | 
			
		||||
    "reference to T", but only in template instantiation, in a method
 | 
			
		||||
    similar to multiple cv-qualifiers.</li>
 | 
			
		||||
  <li>For those of you who are wondering why this shouldn't be const-qualified,
 | 
			
		||||
    remember that references are always implicitly constant (for example, you
 | 
			
		||||
    can't re-assign a reference). Remember also that "const T &"
 | 
			
		||||
    is something completely different. For this reason, cv-qualifiers on
 | 
			
		||||
    template type arguments that are references are ignored.</li>
 | 
			
		||||
</ol>
 | 
			
		||||
<h2>Listing 1</h2>
 | 
			
		||||
<pre>namespace detail{
 | 
			
		||||
 | 
			
		||||
template <bool b>
 | 
			
		||||
struct copier
 | 
			
		||||
{
 | 
			
		||||
   template<typename I1, typename I2>
 | 
			
		||||
   static I2 do_copy(I1 first, 
 | 
			
		||||
                     I1 last, I2 out);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template <bool b>
 | 
			
		||||
template<typename I1, typename I2>
 | 
			
		||||
I2 copier<b>::do_copy(I1 first, 
 | 
			
		||||
                      I1 last, 
 | 
			
		||||
                      I2 out)
 | 
			
		||||
{
 | 
			
		||||
   while(first != last)
 | 
			
		||||
   {
 | 
			
		||||
      *out = *first;
 | 
			
		||||
      ++out;
 | 
			
		||||
      ++first;
 | 
			
		||||
   }
 | 
			
		||||
   return out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <>
 | 
			
		||||
struct copier<true>
 | 
			
		||||
{
 | 
			
		||||
   template<typename I1, typename I2>
 | 
			
		||||
   static I2* do_copy(I1* first, I1* last, I2* out)
 | 
			
		||||
   {
 | 
			
		||||
      memcpy(out, first, (last-first)*sizeof(I2));
 | 
			
		||||
      return out+(last-first);
 | 
			
		||||
   }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template<typename I1, typename I2>
 | 
			
		||||
inline I2 copy(I1 first, I1 last, I2 out)
 | 
			
		||||
{
 | 
			
		||||
   typedef typename 
 | 
			
		||||
    boost::remove_cv<
 | 
			
		||||
     typename std::iterator_traits<I1>
 | 
			
		||||
      ::value_type>::type v1_t;
 | 
			
		||||
 | 
			
		||||
   typedef typename 
 | 
			
		||||
    boost::remove_cv<
 | 
			
		||||
     typename std::iterator_traits<I2>
 | 
			
		||||
      ::value_type>::type v2_t;
 | 
			
		||||
 | 
			
		||||
   enum{ can_opt = 
 | 
			
		||||
      boost::is_same<v1_t, v2_t>::value
 | 
			
		||||
      && boost::is_pointer<I1>::value
 | 
			
		||||
      && boost::is_pointer<I2>::value
 | 
			
		||||
      && boost::
 | 
			
		||||
      has_trivial_assign<v1_t>::value 
 | 
			
		||||
   };
 | 
			
		||||
 | 
			
		||||
   return detail::copier<can_opt>::
 | 
			
		||||
      do_copy(first, last, out);
 | 
			
		||||
}</pre>
 | 
			
		||||
<hr>
 | 
			
		||||
<p><EFBFBD> Copyright John Maddock and Steve Cleary, 2000</p>
 | 
			
		||||
 | 
			
		||||
</body>
 | 
			
		||||
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										464
									
								
								iterator_adaptor_test.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										464
									
								
								iterator_adaptor_test.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,464 @@
 | 
			
		||||
//  Demonstrate and test boost/operators.hpp on std::iterators  -------------//
 | 
			
		||||
 | 
			
		||||
//  (C) Copyright Jeremy Siek 1999. Permission to copy, use, modify,
 | 
			
		||||
//  sell and distribute this software is granted provided this
 | 
			
		||||
//  copyright notice appears in all copies. This software is provided
 | 
			
		||||
//  "as is" without express or implied warranty, and with no claim as
 | 
			
		||||
//  to its suitability for any purpose.
 | 
			
		||||
 | 
			
		||||
//  See http://www.boost.org for most recent version including documentation.
 | 
			
		||||
 | 
			
		||||
//  Revision History
 | 
			
		||||
//  04 Mar 01 Workaround for Borland (Dave Abrahams)
 | 
			
		||||
//  19 Feb 01 Take adavantage of improved iterator_traits to do more tests
 | 
			
		||||
//            on MSVC. Hack around an MSVC-with-STLport internal compiler
 | 
			
		||||
//            error. (David Abrahams)
 | 
			
		||||
//  11 Feb 01 Added test of operator-> for forward and input iterators.
 | 
			
		||||
//            (Jeremy Siek)
 | 
			
		||||
//  11 Feb 01 Borland fixes (David Abrahams)
 | 
			
		||||
//  10 Feb 01 Use new adaptors interface. (David Abrahams)
 | 
			
		||||
//  10 Feb 01 Use new filter_ interface. (David Abrahams)
 | 
			
		||||
//  09 Feb 01 Use new reverse_ and indirect_ interfaces. Replace
 | 
			
		||||
//            BOOST_NO_STD_ITERATOR_TRAITS with
 | 
			
		||||
//            BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION to prove we've
 | 
			
		||||
//            normalized to core compiler capabilities (David Abrahams)
 | 
			
		||||
//  08 Feb 01 Use Jeremy's new make_reverse_iterator form; add more
 | 
			
		||||
//            comprehensive testing. Force-decay array function arguments to
 | 
			
		||||
//            pointers.
 | 
			
		||||
//  07 Feb 01 Added tests for the make_xxx_iterator() helper functions.
 | 
			
		||||
//            (Jeremy Siek)
 | 
			
		||||
//  07 Feb 01 Replaced use of xxx_pair_generator with xxx_generator where
 | 
			
		||||
//            possible (which was all but the projection iterator).
 | 
			
		||||
//            (Jeremy Siek)
 | 
			
		||||
//  06 Feb 01 Removed now-defaulted template arguments where possible
 | 
			
		||||
//            Updated names to correspond to new generator naming convention.
 | 
			
		||||
//            Added a trivial test for make_transform_iterator().
 | 
			
		||||
//            Gave traits for const iterators a mutable value_type, per std.
 | 
			
		||||
//            Resurrected my original tests for indirect iterators.
 | 
			
		||||
//            (David Abrahams)
 | 
			
		||||
//  04 Feb 01 Fix for compilers without standard iterator_traits
 | 
			
		||||
//            (David Abrahams)
 | 
			
		||||
//  13 Jun 00 Added const version of the iterator tests (Jeremy Siek)
 | 
			
		||||
//  12 Dec 99 Initial version with iterator operators (Jeremy Siek)
 | 
			
		||||
 | 
			
		||||
#include <boost/config.hpp>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <functional>
 | 
			
		||||
 | 
			
		||||
#include <boost/iterator_adaptors.hpp>
 | 
			
		||||
#include <boost/pending/iterator_tests.hpp>
 | 
			
		||||
#include <boost/pending/integer_range.hpp>
 | 
			
		||||
#include <boost/concept_archetype.hpp>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <deque>
 | 
			
		||||
#include <set>
 | 
			
		||||
 | 
			
		||||
struct my_iterator_tag : public std::random_access_iterator_tag { };
 | 
			
		||||
 | 
			
		||||
using boost::dummyT;
 | 
			
		||||
 | 
			
		||||
struct my_iter_traits {
 | 
			
		||||
  typedef dummyT value_type;
 | 
			
		||||
  typedef dummyT* pointer;
 | 
			
		||||
  typedef dummyT& reference;
 | 
			
		||||
  typedef my_iterator_tag iterator_category;
 | 
			
		||||
  typedef std::ptrdiff_t difference_type;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct my_const_iter_traits {
 | 
			
		||||
  typedef dummyT value_type;
 | 
			
		||||
  typedef const dummyT* pointer;
 | 
			
		||||
  typedef const dummyT& reference;
 | 
			
		||||
  typedef my_iterator_tag iterator_category;
 | 
			
		||||
  typedef std::ptrdiff_t difference_type;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef boost::iterator_adaptor<dummyT*, 
 | 
			
		||||
  boost::default_iterator_policies, dummyT> my_iterator;
 | 
			
		||||
 | 
			
		||||
typedef boost::iterator_adaptor<const dummyT*, 
 | 
			
		||||
  boost::default_iterator_policies, const dummyT> const_my_iterator;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
struct mult_functor {
 | 
			
		||||
  typedef int result_type;
 | 
			
		||||
  typedef int argument_type;
 | 
			
		||||
  // Functors used with transform_iterator must be
 | 
			
		||||
  // DefaultConstructible, as the transform_iterator must be
 | 
			
		||||
  // DefaultConstructible to satisfy the requirements for
 | 
			
		||||
  // TrivialIterator.
 | 
			
		||||
  mult_functor() { }
 | 
			
		||||
  mult_functor(int aa) : a(aa) { }
 | 
			
		||||
  int operator()(int b) const { return a * b; }
 | 
			
		||||
  int a;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template <class Pair>
 | 
			
		||||
struct select1st_ 
 | 
			
		||||
  : public std::unary_function<Pair, typename Pair::first_type>
 | 
			
		||||
{
 | 
			
		||||
  const typename Pair::first_type& operator()(const Pair& x) const {
 | 
			
		||||
    return x.first;
 | 
			
		||||
  }
 | 
			
		||||
  typename Pair::first_type& operator()(Pair& x) const {
 | 
			
		||||
    return x.first;
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct one_or_four {
 | 
			
		||||
  bool operator()(dummyT x) const {
 | 
			
		||||
    return x.foo() == 1 || x.foo() == 4;
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef std::deque<int> storage;
 | 
			
		||||
typedef std::deque<int*> pointer_deque;
 | 
			
		||||
typedef std::set<storage::iterator> iterator_set;
 | 
			
		||||
 | 
			
		||||
void more_indirect_iterator_tests()
 | 
			
		||||
{
 | 
			
		||||
// For some reason all heck breaks loose in the compiler under these conditions.
 | 
			
		||||
#if !defined(BOOST_MSVC) || !defined(__STL_DEBUG)
 | 
			
		||||
    storage store(1000);
 | 
			
		||||
    std::generate(store.begin(), store.end(), rand);
 | 
			
		||||
    
 | 
			
		||||
    pointer_deque ptr_deque;
 | 
			
		||||
    iterator_set iter_set;
 | 
			
		||||
 | 
			
		||||
    for (storage::iterator p = store.begin(); p != store.end(); ++p)
 | 
			
		||||
    {
 | 
			
		||||
        ptr_deque.push_back(&*p);
 | 
			
		||||
        iter_set.insert(p);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    typedef boost::indirect_iterator_pair_generator<
 | 
			
		||||
        pointer_deque::iterator
 | 
			
		||||
#ifdef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
 | 
			
		||||
        , int
 | 
			
		||||
#endif
 | 
			
		||||
    > IndirectDeque;
 | 
			
		||||
 | 
			
		||||
    IndirectDeque::iterator db(ptr_deque.begin());
 | 
			
		||||
    IndirectDeque::iterator de(ptr_deque.end());
 | 
			
		||||
    assert(static_cast<std::size_t>(de - db) == store.size());
 | 
			
		||||
    assert(db + store.size() == de);
 | 
			
		||||
    IndirectDeque::const_iterator dci(db);
 | 
			
		||||
    assert(db == dci);
 | 
			
		||||
    assert(dci == db);
 | 
			
		||||
    assert(dci != de);
 | 
			
		||||
    assert(dci < de);
 | 
			
		||||
    assert(dci <= de);
 | 
			
		||||
    assert(de >= dci);
 | 
			
		||||
    assert(de > dci);
 | 
			
		||||
    dci = de;
 | 
			
		||||
    assert(dci == de);
 | 
			
		||||
 | 
			
		||||
    boost::random_access_iterator_test(db + 1, store.size() - 1, boost::next(store.begin()));
 | 
			
		||||
    
 | 
			
		||||
    *db = 999;
 | 
			
		||||
    assert(store.front() == 999);
 | 
			
		||||
 | 
			
		||||
    typedef boost::indirect_iterator_generator<
 | 
			
		||||
        iterator_set::iterator
 | 
			
		||||
#ifdef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
 | 
			
		||||
        , int
 | 
			
		||||
#endif
 | 
			
		||||
        >::type indirect_set_iterator;
 | 
			
		||||
 | 
			
		||||
    typedef boost::indirect_iterator_generator<
 | 
			
		||||
        iterator_set::iterator,
 | 
			
		||||
        const int
 | 
			
		||||
        >::type const_indirect_set_iterator;
 | 
			
		||||
 | 
			
		||||
    indirect_set_iterator sb(iter_set.begin());
 | 
			
		||||
    indirect_set_iterator se(iter_set.end());
 | 
			
		||||
    const_indirect_set_iterator sci(iter_set.begin());
 | 
			
		||||
    assert(sci == sb);
 | 
			
		||||
    assert(sci != se);
 | 
			
		||||
    sci = se;
 | 
			
		||||
    assert(sci == se);
 | 
			
		||||
    
 | 
			
		||||
    *boost::prior(se) = 888;
 | 
			
		||||
    assert(store.back() == 888);
 | 
			
		||||
    assert(std::equal(sb, se, store.begin()));
 | 
			
		||||
 | 
			
		||||
    boost::bidirectional_iterator_test(boost::next(sb), store[1], store[2]);
 | 
			
		||||
    assert(std::equal(db, de, store.begin()));
 | 
			
		||||
#endif    
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
main()
 | 
			
		||||
{
 | 
			
		||||
  dummyT array[] = { dummyT(0), dummyT(1), dummyT(2), 
 | 
			
		||||
                     dummyT(3), dummyT(4), dummyT(5) };
 | 
			
		||||
  const int N = sizeof(array)/sizeof(dummyT);
 | 
			
		||||
 | 
			
		||||
  // sanity check, if this doesn't pass the test is buggy
 | 
			
		||||
  boost::random_access_iterator_test(array,N,array);
 | 
			
		||||
 | 
			
		||||
  // Check that the policy concept checks and the default policy
 | 
			
		||||
  // implementation match up.
 | 
			
		||||
  boost::function_requires< 
 | 
			
		||||
     boost::RandomAccessIteratorPoliciesConcept<
 | 
			
		||||
       boost::default_iterator_policies, int*,
 | 
			
		||||
       boost::iterator<std::random_access_iterator_tag, int, std::ptrdiff_t,
 | 
			
		||||
                      int*, int&>
 | 
			
		||||
      > >();
 | 
			
		||||
 | 
			
		||||
  // Test the iterator_adaptor
 | 
			
		||||
  {
 | 
			
		||||
    my_iterator i(array);
 | 
			
		||||
    boost::random_access_iterator_test(i, N, array);
 | 
			
		||||
    
 | 
			
		||||
    const_my_iterator j(array);
 | 
			
		||||
    boost::random_access_iterator_test(j, N, array);
 | 
			
		||||
    boost::const_nonconst_iterator_test(i, ++j);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Test transform_iterator
 | 
			
		||||
  {
 | 
			
		||||
    int x[N], y[N];
 | 
			
		||||
    for (int k = 0; k < N; ++k)
 | 
			
		||||
      x[k] = k;
 | 
			
		||||
    std::copy(x, x + N, y);
 | 
			
		||||
 | 
			
		||||
    for (int k2 = 0; k2 < N; ++k2)
 | 
			
		||||
      x[k2] = x[k2] * 2;
 | 
			
		||||
 | 
			
		||||
    boost::transform_iterator_generator<mult_functor, int*>::type
 | 
			
		||||
      i(y, mult_functor(2));
 | 
			
		||||
    boost::input_iterator_test(i, x[0], x[1]);
 | 
			
		||||
    boost::input_iterator_test(boost::make_transform_iterator(&y[0], mult_functor(2)), x[0], x[1]);
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  // Test indirect_iterator_generator
 | 
			
		||||
  {
 | 
			
		||||
    dummyT* ptr[N];
 | 
			
		||||
    for (int k = 0; k < N; ++k)
 | 
			
		||||
      ptr[k] = array + k;
 | 
			
		||||
 | 
			
		||||
    typedef boost::indirect_iterator_generator<dummyT**
 | 
			
		||||
#ifdef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
 | 
			
		||||
        , dummyT
 | 
			
		||||
#endif
 | 
			
		||||
      >::type indirect_iterator;
 | 
			
		||||
 | 
			
		||||
    typedef boost::indirect_iterator_generator<dummyT**, const dummyT>::type const_indirect_iterator;
 | 
			
		||||
 | 
			
		||||
    indirect_iterator i(ptr);
 | 
			
		||||
    boost::random_access_iterator_test(i, N, array);
 | 
			
		||||
 | 
			
		||||
#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
 | 
			
		||||
    boost::random_access_iterator_test(boost::make_indirect_iterator(ptr), N, array);
 | 
			
		||||
#endif
 | 
			
		||||
    
 | 
			
		||||
    // check operator->
 | 
			
		||||
    assert((*i).m_x == i->foo());
 | 
			
		||||
 | 
			
		||||
    const_indirect_iterator j(ptr);
 | 
			
		||||
    boost::random_access_iterator_test(j, N, array);
 | 
			
		||||
 | 
			
		||||
    dummyT*const* const_ptr = ptr;
 | 
			
		||||
    
 | 
			
		||||
#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
 | 
			
		||||
    boost::random_access_iterator_test(boost::make_indirect_iterator(const_ptr), N, array);
 | 
			
		||||
#endif
 | 
			
		||||
    boost::const_nonconst_iterator_test(i, ++j);
 | 
			
		||||
 | 
			
		||||
    more_indirect_iterator_tests();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Test projection_iterator_pair_generator
 | 
			
		||||
  {    
 | 
			
		||||
    typedef std::pair<dummyT,dummyT> Pair;
 | 
			
		||||
    Pair pair_array[N];
 | 
			
		||||
    for (int k = 0; k < N; ++k)
 | 
			
		||||
      pair_array[k].first = array[k];
 | 
			
		||||
 | 
			
		||||
    typedef boost::projection_iterator_pair_generator<select1st_<Pair>,
 | 
			
		||||
      Pair*, const Pair*
 | 
			
		||||
      > Projection;
 | 
			
		||||
    
 | 
			
		||||
    Projection::iterator i(pair_array);
 | 
			
		||||
    boost::random_access_iterator_test(i, N, array);
 | 
			
		||||
 | 
			
		||||
    boost::random_access_iterator_test(boost::make_projection_iterator(pair_array, select1st_<Pair>()), N, array);    
 | 
			
		||||
    boost::random_access_iterator_test(boost::make_projection_iterator< select1st_<Pair> >(pair_array), N, array);    
 | 
			
		||||
 | 
			
		||||
    Projection::const_iterator j(pair_array);
 | 
			
		||||
    boost::random_access_iterator_test(j, N, array);
 | 
			
		||||
 | 
			
		||||
    boost::random_access_iterator_test(boost::make_const_projection_iterator(pair_array, select1st_<Pair>()), N, array);
 | 
			
		||||
    boost::random_access_iterator_test(boost::make_const_projection_iterator<select1st_<Pair> >(pair_array), N, array);
 | 
			
		||||
 | 
			
		||||
    boost::const_nonconst_iterator_test(i, ++j);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Test reverse_iterator_generator
 | 
			
		||||
  {
 | 
			
		||||
    dummyT reversed[N];
 | 
			
		||||
    std::copy(array, array + N, reversed);
 | 
			
		||||
    std::reverse(reversed, reversed + N);
 | 
			
		||||
    
 | 
			
		||||
    typedef boost::reverse_iterator_generator<dummyT*
 | 
			
		||||
#ifdef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
 | 
			
		||||
        , dummyT
 | 
			
		||||
#endif
 | 
			
		||||
      >::type reverse_iterator;
 | 
			
		||||
    
 | 
			
		||||
    reverse_iterator i(reversed + N);
 | 
			
		||||
    boost::random_access_iterator_test(i, N, array);
 | 
			
		||||
 | 
			
		||||
#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
 | 
			
		||||
    boost::random_access_iterator_test(boost::make_reverse_iterator(reversed + N), N, array);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    typedef boost::reverse_iterator_generator<const dummyT*
 | 
			
		||||
#ifdef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
 | 
			
		||||
        , const dummyT
 | 
			
		||||
#endif
 | 
			
		||||
      >::type const_reverse_iterator;
 | 
			
		||||
    
 | 
			
		||||
    const_reverse_iterator j(reversed + N);
 | 
			
		||||
    boost::random_access_iterator_test(j, N, array);
 | 
			
		||||
 | 
			
		||||
    const dummyT* const_reversed = reversed;
 | 
			
		||||
    
 | 
			
		||||
#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
 | 
			
		||||
    boost::random_access_iterator_test(boost::make_reverse_iterator(const_reversed + N), N, array);
 | 
			
		||||
#endif
 | 
			
		||||
    
 | 
			
		||||
    boost::const_nonconst_iterator_test(i, ++j);    
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Test reverse_iterator_generator again, with traits fully deducible on all platforms
 | 
			
		||||
  {
 | 
			
		||||
    std::deque<dummyT> reversed_container;
 | 
			
		||||
    std::reverse_copy(array, array + N, std::back_inserter(reversed_container));
 | 
			
		||||
    const std::deque<dummyT>::iterator reversed = reversed_container.begin();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    typedef boost::reverse_iterator_generator<
 | 
			
		||||
        std::deque<dummyT>::iterator>::type reverse_iterator;
 | 
			
		||||
    typedef boost::reverse_iterator_generator<
 | 
			
		||||
        std::deque<dummyT>::const_iterator, const dummyT>::type const_reverse_iterator;
 | 
			
		||||
 | 
			
		||||
    // MSVC/STLport gives an INTERNAL COMPILER ERROR when any computation
 | 
			
		||||
    // (e.g. "reversed + N") is used in the constructor below.
 | 
			
		||||
    const std::deque<dummyT>::iterator finish = reversed_container.end();
 | 
			
		||||
    reverse_iterator i(finish);
 | 
			
		||||
    
 | 
			
		||||
    boost::random_access_iterator_test(i, N, array);
 | 
			
		||||
    boost::random_access_iterator_test(boost::make_reverse_iterator(reversed + N), N, array);
 | 
			
		||||
 | 
			
		||||
    const_reverse_iterator j = reverse_iterator(finish);
 | 
			
		||||
    boost::random_access_iterator_test(j, N, array);
 | 
			
		||||
 | 
			
		||||
    const std::deque<dummyT>::const_iterator const_reversed = reversed;
 | 
			
		||||
    boost::random_access_iterator_test(boost::make_reverse_iterator(const_reversed + N), N, array);
 | 
			
		||||
    
 | 
			
		||||
    // Many compilers' builtin deque iterators don't interoperate well, though
 | 
			
		||||
    // STLport fixes that problem.
 | 
			
		||||
#if defined(__SGI_STL_PORT) || !defined(__GNUC__) && !defined(__BORLANDC__) && !defined(BOOST_MSVC)
 | 
			
		||||
    boost::const_nonconst_iterator_test(i, ++j);
 | 
			
		||||
#endif
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  // Test integer_range's iterators
 | 
			
		||||
  {
 | 
			
		||||
    int int_array[] = { 0, 1, 2, 3, 4, 5 };
 | 
			
		||||
    boost::integer_range<int> r(0, 5);
 | 
			
		||||
    boost::random_access_iterator_test(r.begin(), r.size(), int_array);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Test filter iterator
 | 
			
		||||
  {
 | 
			
		||||
    // Using typedefs for filter_gen::type and filter_gen::policies_type
 | 
			
		||||
    // confused Borland terribly.
 | 
			
		||||
    typedef boost::detail::non_bidirectional_category<dummyT*>::type category;
 | 
			
		||||
    
 | 
			
		||||
    typedef ::boost::filter_iterator_generator<one_or_four, dummyT*
 | 
			
		||||
#ifdef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
 | 
			
		||||
        , dummyT
 | 
			
		||||
#endif
 | 
			
		||||
        > filter_iter_gen;
 | 
			
		||||
 | 
			
		||||
#ifndef __BORLANDC__
 | 
			
		||||
    typedef filter_iter_gen::type filter_iter;
 | 
			
		||||
#else
 | 
			
		||||
# define filter_iter filter_iter_gen::type // Borland has a problem with the above
 | 
			
		||||
#endif
 | 
			
		||||
    filter_iter i(array, filter_iter::policies_type(one_or_four(), array + N));
 | 
			
		||||
    boost::forward_iterator_test(i, dummyT(1), dummyT(4));
 | 
			
		||||
 | 
			
		||||
    enum { is_forward = boost::is_same<
 | 
			
		||||
           filter_iter::iterator_category,
 | 
			
		||||
           std::forward_iterator_tag>::value };
 | 
			
		||||
    BOOST_STATIC_ASSERT(is_forward);
 | 
			
		||||
 | 
			
		||||
    // On compilers not supporting partial specialization, we can do more type
 | 
			
		||||
    // deduction with deque iterators than with pointers... unless the library
 | 
			
		||||
    // is broken ;-(
 | 
			
		||||
#if !defined(BOOST_MSVC) || defined(__SGI_STL_PORT)
 | 
			
		||||
    std::deque<dummyT> array2;
 | 
			
		||||
    std::copy(array+0, array+N, std::back_inserter(array2));
 | 
			
		||||
    boost::forward_iterator_test(
 | 
			
		||||
        boost::make_filter_iterator(array2.begin(), array2.end(), one_or_four()),
 | 
			
		||||
        dummyT(1), dummyT(4));
 | 
			
		||||
 | 
			
		||||
    boost::forward_iterator_test(
 | 
			
		||||
        boost::make_filter_iterator<one_or_four>(array2.begin(), array2.end()),
 | 
			
		||||
        dummyT(1), dummyT(4));
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if !defined(BOOST_MSVC) // This just freaks MSVC out completely
 | 
			
		||||
    boost::forward_iterator_test(
 | 
			
		||||
        boost::make_filter_iterator<one_or_four>(
 | 
			
		||||
            boost::make_reverse_iterator(array2.end()),
 | 
			
		||||
            boost::make_reverse_iterator(array2.begin())
 | 
			
		||||
            ),
 | 
			
		||||
        dummyT(4), dummyT(1));
 | 
			
		||||
#endif
 | 
			
		||||
    
 | 
			
		||||
#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
 | 
			
		||||
    boost::forward_iterator_test(
 | 
			
		||||
        boost::make_filter_iterator(array+0, array+N, one_or_four()),
 | 
			
		||||
        dummyT(1), dummyT(4));
 | 
			
		||||
 | 
			
		||||
    boost::forward_iterator_test(
 | 
			
		||||
        boost::make_filter_iterator<one_or_four>(array, array + N),
 | 
			
		||||
        dummyT(1), dummyT(4));
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // check operator-> with a forward iterator
 | 
			
		||||
  {
 | 
			
		||||
    boost::forward_iterator_archetype<dummyT> forward_iter;
 | 
			
		||||
    typedef boost::iterator_adaptor<boost::forward_iterator_archetype<dummyT>,
 | 
			
		||||
      boost::default_iterator_policies,
 | 
			
		||||
      dummyT, const dummyT&, const dummyT*, 
 | 
			
		||||
      std::forward_iterator_tag, std::ptrdiff_t> adaptor_type;
 | 
			
		||||
    adaptor_type i(forward_iter);
 | 
			
		||||
    if (0) // don't do this, just make sure it compiles
 | 
			
		||||
      assert((*i).m_x == i->foo());      
 | 
			
		||||
  }
 | 
			
		||||
  // check operator-> with an input iterator
 | 
			
		||||
  {
 | 
			
		||||
    boost::input_iterator_archetype<dummyT> input_iter;
 | 
			
		||||
    typedef boost::iterator_adaptor<boost::input_iterator_archetype<dummyT>,
 | 
			
		||||
      boost::default_iterator_policies,
 | 
			
		||||
      dummyT, const dummyT&, const dummyT*, 
 | 
			
		||||
      std::input_iterator_tag, std::ptrdiff_t> adaptor_type;
 | 
			
		||||
    adaptor_type i(input_iter);
 | 
			
		||||
    if (0) // don't do this, just make sure it compiles
 | 
			
		||||
      assert((*i).m_x == i->foo());      
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  std::cout << "test successful " << std::endl;
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										331
									
								
								reverse_iterator.htm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										331
									
								
								reverse_iterator.htm
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,331 @@
 | 
			
		||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2//EN">
 | 
			
		||||
 | 
			
		||||
<html>
 | 
			
		||||
<head>
 | 
			
		||||
    <meta name="generator" content="HTML Tidy, see www.w3.org">
 | 
			
		||||
    <meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
 | 
			
		||||
    <meta name="GENERATOR" content="Microsoft FrontPage 4.0">
 | 
			
		||||
    <meta name="ProgId" content="FrontPage.Editor.Document">
 | 
			
		||||
 | 
			
		||||
    <title>Reverse Iterator Adaptor Documentation</title>
 | 
			
		||||
</head>
 | 
			
		||||
 | 
			
		||||
<body bgcolor="#FFFFFF" text="#000000">
 | 
			
		||||
    <img src="../../c++boost.gif" alt="c++boost.gif (8819 bytes)" align=
 | 
			
		||||
    "center" width="277" height="86"> 
 | 
			
		||||
 | 
			
		||||
    <h1>Reverse Iterator Adaptor</h1>
 | 
			
		||||
    Defined in header <a href=
 | 
			
		||||
    "../../boost/iterator_adaptors.hpp">boost/iterator_adaptors.hpp</a> 
 | 
			
		||||
 | 
			
		||||
    <p>The reverse iterator adaptor flips the direction of a base iterator's
 | 
			
		||||
    motion. Invoking <tt>operator++()</tt> moves the base iterator backward and
 | 
			
		||||
    invoking <tt>operator--()</tt> moves the base iterator forward. The Boost
 | 
			
		||||
    reverse iterator adaptor is better to use than the
 | 
			
		||||
    <tt>std::reverse_iterator</tt> class in situations where pairs of
 | 
			
		||||
    mutable/constant iterators are needed (e.g., in containers) because
 | 
			
		||||
    comparisons and conversions between the mutable and const versions are
 | 
			
		||||
    implemented correctly.
 | 
			
		||||
 | 
			
		||||
    <h2>Synopsis</h2>
 | 
			
		||||
<pre>
 | 
			
		||||
namespace boost {
 | 
			
		||||
  template <class <a href=
 | 
			
		||||
"http://www.sgi.com/tech/stl/BidirectionalIterator.html">BidirectionalIterator</a>,
 | 
			
		||||
            class Value, class Reference, class Pointer, class Category, class Distance>
 | 
			
		||||
  struct reverse_iterator_generator;
 | 
			
		||||
  
 | 
			
		||||
  template <class <a href=
 | 
			
		||||
"http://www.sgi.com/tech/stl/BidirectionalIterator.html">BidirectionalIterator</a>>
 | 
			
		||||
  typename reverse_iterator_generator<BidirectionalIterator>::type
 | 
			
		||||
  make_reverse_iterator(BidirectionalIterator base)  
 | 
			
		||||
}
 | 
			
		||||
</pre>
 | 
			
		||||
    <hr>
 | 
			
		||||
 | 
			
		||||
    <h2><a name="reverse_iterator_generator">The Reverse Iterator Type
 | 
			
		||||
    Generator</a></h2>
 | 
			
		||||
    The <tt>reverse_iterator_generator</tt> template is a <a href=
 | 
			
		||||
    "../../more/generic_programming.html#type_generator">generator</a> of
 | 
			
		||||
    reverse iterator types. The main template parameter for this class is the
 | 
			
		||||
    base <tt>BidirectionalIterator</tt> type that is being adapted. In most
 | 
			
		||||
    cases the associated types of the base iterator can be deduced using
 | 
			
		||||
    <tt>std::iterator_traits</tt>, but in some situations the user may want to
 | 
			
		||||
    override these types, so there are also template parameters for the base
 | 
			
		||||
    iterator's associated types. 
 | 
			
		||||
 | 
			
		||||
    <blockquote>
 | 
			
		||||
<pre>
 | 
			
		||||
template <class <a href=
 | 
			
		||||
"http://www.sgi.com/tech/stl/BidirectionalIterator.html">BidirectionalIterator</a>,
 | 
			
		||||
          class Value, class Reference, class Pointer, class Category, class Distance>
 | 
			
		||||
class reverse_iterator_generator
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
  typedef <tt><a href=
 | 
			
		||||
"./iterator_adaptors.htm#iterator_adaptor">iterator_adaptor</a><...></tt> type; // the resulting reverse iterator type 
 | 
			
		||||
};
 | 
			
		||||
</pre>
 | 
			
		||||
    </blockquote>
 | 
			
		||||
 | 
			
		||||
    <h3>Example</h3>
 | 
			
		||||
    In this example we sort a sequence of letters and then output the sequence
 | 
			
		||||
    in descending order using reverse iterators. 
 | 
			
		||||
 | 
			
		||||
    <blockquote>
 | 
			
		||||
<pre>
 | 
			
		||||
#include <boost/config.hpp>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <boost/iterator_adaptors.hpp>
 | 
			
		||||
 | 
			
		||||
int main(int, char*[])
 | 
			
		||||
{
 | 
			
		||||
  char letters[] = "hello world!";
 | 
			
		||||
  const int N = sizeof(letters)/sizeof(char) - 1;
 | 
			
		||||
  std::cout << "original sequence of letters:\t"
 | 
			
		||||
      << letters << std::endl;
 | 
			
		||||
 | 
			
		||||
  std::sort(letters, letters + N);
 | 
			
		||||
 | 
			
		||||
  // Use reverse_iterator_generator to print a sequence
 | 
			
		||||
  // of letters in reverse order.
 | 
			
		||||
  
 | 
			
		||||
  boost::reverse_iterator_generator<char*>::type
 | 
			
		||||
    reverse_letters_first(letters + N),
 | 
			
		||||
    reverse_letters_last(letters);
 | 
			
		||||
 | 
			
		||||
  std::cout << "letters in descending order:\t";
 | 
			
		||||
  std::copy(reverse_letters_first, reverse_letters_last,
 | 
			
		||||
      std::ostream_iterator<char>(std::cout));
 | 
			
		||||
  std::cout << std::endl;
 | 
			
		||||
 | 
			
		||||
  // to be continued...
 | 
			
		||||
</pre>
 | 
			
		||||
    </blockquote>
 | 
			
		||||
    The output is: 
 | 
			
		||||
 | 
			
		||||
    <blockquote>
 | 
			
		||||
<pre>
 | 
			
		||||
original sequence of letters: hello world!
 | 
			
		||||
letters in descending order:  wroolllhed! 
 | 
			
		||||
</pre>
 | 
			
		||||
    </blockquote>
 | 
			
		||||
 | 
			
		||||
    <h3>Template Parameters</h3>
 | 
			
		||||
 | 
			
		||||
    <table border>
 | 
			
		||||
      <tr>
 | 
			
		||||
        <th>Parameter
 | 
			
		||||
 | 
			
		||||
        <th>Description
 | 
			
		||||
 | 
			
		||||
      <tr>
 | 
			
		||||
        <td><tt><a href=
 | 
			
		||||
        "http://www.sgi.com/tech/stl/BidirectionalIterator.html">BidirectionalIterator</a></tt>
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        <td>The iterator type being wrapped.
 | 
			
		||||
 | 
			
		||||
      <tr>
 | 
			
		||||
        <td><tt>Value</tt> 
 | 
			
		||||
 | 
			
		||||
        <td>The value-type of the base iterator and the resulting reverse
 | 
			
		||||
        iterator.<br>
 | 
			
		||||
         <b>Default:</b><tt>std::iterator_traits<BidirectionalIterator>::value_type</tt>
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
      <tr>
 | 
			
		||||
        <td><tt>Reference</tt> 
 | 
			
		||||
 | 
			
		||||
        <td>The <tt>reference</tt> type of the resulting iterator, and in
 | 
			
		||||
        particular, the result type of <tt>operator*()</tt>.<br>
 | 
			
		||||
         <b>Default:</b> If <tt>Value</tt> is supplied, <tt>Value&</tt> is
 | 
			
		||||
        used. Otherwise
 | 
			
		||||
        <tt>std::iterator_traits<BidirectionalIterator>::reference</tt>
 | 
			
		||||
        is used.
 | 
			
		||||
 | 
			
		||||
      <tr>
 | 
			
		||||
        <td><tt>Pointer</tt> 
 | 
			
		||||
 | 
			
		||||
        <td>The <tt>pointer</tt> type of the resulting iterator, and in
 | 
			
		||||
        particular, the result type of <tt>operator->()</tt>.<br>
 | 
			
		||||
         <b>Default:</b> If <tt>Value</tt> was supplied, then <tt>Value*</tt>,
 | 
			
		||||
        otherwise
 | 
			
		||||
        <tt>std::iterator_traits<BidirectionalIterator>::pointer</tt>.
 | 
			
		||||
 | 
			
		||||
      <tr>
 | 
			
		||||
        <td><tt>Category</tt> 
 | 
			
		||||
 | 
			
		||||
        <td>The <tt>iterator_category</tt> type for the resulting iterator.<br>
 | 
			
		||||
         <b>Default:</b>
 | 
			
		||||
        <tt>std::iterator_traits<BidirectionalIterator>::iterator_category</tt>
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
      <tr>
 | 
			
		||||
        <td><tt>Distance</tt> 
 | 
			
		||||
 | 
			
		||||
        <td>The <tt>difference_type</tt> for the resulting iterator.<br>
 | 
			
		||||
         <b>Default:</b>
 | 
			
		||||
        <tt>std::iterator_traits<BidirectionalIterator&gt::difference_type</tt>
 | 
			
		||||
        
 | 
			
		||||
    </table>
 | 
			
		||||
 | 
			
		||||
    <h3>Concept Model</h3>
 | 
			
		||||
    The indirect iterator will model whichever <a href=
 | 
			
		||||
    "http://www.sgi.com/tech/stl/Iterators.html">standard iterator concept
 | 
			
		||||
    category</a> is modeled by the base iterator. Thus, if the base iterator is
 | 
			
		||||
    a model of <a href=
 | 
			
		||||
    "http://www.sgi.com/tech/stl/RandomAccessIterator.html">Random Access
 | 
			
		||||
    Iterator</a> then so is the resulting indirect iterator. If the base
 | 
			
		||||
    iterator models a more restrictive concept, the resulting indirect iterator
 | 
			
		||||
    will model the same concept. The base iterator must be at least a <a href=
 | 
			
		||||
    "http://www.sgi.com/tech/stl/BidirectionalIterator.html">Bidirectional
 | 
			
		||||
    Iterator</a> 
 | 
			
		||||
 | 
			
		||||
    <h3>Members</h3>
 | 
			
		||||
    The reverse iterator type implements the member functions and operators
 | 
			
		||||
    required of the <a href=
 | 
			
		||||
    "http://www.sgi.com/tech/stl/RandomAccessIterator.html">Random Access
 | 
			
		||||
    Iterator</a> concept. In addition it has the following constructor: 
 | 
			
		||||
 | 
			
		||||
    <blockquote>
 | 
			
		||||
<pre>
 | 
			
		||||
reverse_iterator_generator::type(const BidirectionalIterator& it)
 | 
			
		||||
</pre>
 | 
			
		||||
    </blockquote>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    <br>
 | 
			
		||||
     <br>
 | 
			
		||||
     
 | 
			
		||||
    <hr>
 | 
			
		||||
 | 
			
		||||
    <p>
 | 
			
		||||
 | 
			
		||||
    <h2><a name="make_reverse_iterator">The Reverse Iterator Object
 | 
			
		||||
    Generator</a></h2>
 | 
			
		||||
    The <tt>make_reverse_iterator()</tt> function provides a more convenient
 | 
			
		||||
    way to create reverse iterator objects. The function saves the user the
 | 
			
		||||
    trouble of explicitly writing out the iterator types. 
 | 
			
		||||
 | 
			
		||||
    <blockquote>
 | 
			
		||||
<pre>
 | 
			
		||||
template <class BidirectionalIterator>
 | 
			
		||||
typename reverse_iterator_generator<BidirectionalIterator>::type
 | 
			
		||||
make_reverse_iterator(BidirectionalIterator base);
 | 
			
		||||
</pre>
 | 
			
		||||
    </blockquote>
 | 
			
		||||
 | 
			
		||||
    <h3>Example</h3>
 | 
			
		||||
    In this part of the example we use <tt>make_reverse_iterator()</tt> to
 | 
			
		||||
    print the sequence of letters in reverse-reverse order, which is the
 | 
			
		||||
    original order. 
 | 
			
		||||
 | 
			
		||||
    <blockquote>
 | 
			
		||||
<pre>
 | 
			
		||||
  // continuing from the previous example...
 | 
			
		||||
 | 
			
		||||
  std::cout << "letters in ascending order:\t";
 | 
			
		||||
  std::copy(boost::make_reverse_iterator(reverse_letters_last),
 | 
			
		||||
      boost::make_reverse_iterator(reverse_letters_first),
 | 
			
		||||
      std::ostream_iterator<char>(std::cout));
 | 
			
		||||
  std::cout << std::endl;
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
</pre>
 | 
			
		||||
    </blockquote>
 | 
			
		||||
    The output is: 
 | 
			
		||||
 | 
			
		||||
    <blockquote>
 | 
			
		||||
<pre>
 | 
			
		||||
letters in ascending order:  !dehllloorw
 | 
			
		||||
</pre>
 | 
			
		||||
    </blockquote>
 | 
			
		||||
    <hr>
 | 
			
		||||
 | 
			
		||||
    <h2><a name="interactions">Constant/Mutable Iterator Interactions</a></h2>
 | 
			
		||||
 | 
			
		||||
    <p>One failing of the standard <tt><a
 | 
			
		||||
    href="http://www.sgi.com/tech/stl/ReverseIterator.html">reverse_iterator</a></tt>
 | 
			
		||||
    adaptor is that it doesn't properly support interactions between adapted
 | 
			
		||||
    <tt>const</tt> and non-<tt>const</tt> iterators. For example:
 | 
			
		||||
<blockquote>
 | 
			
		||||
<pre>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
template <class T> void convert(T x) {}
 | 
			
		||||
 | 
			
		||||
// Test interactions of a matched pair of random access iterators
 | 
			
		||||
template <class Iterator, class ConstIterator>
 | 
			
		||||
void test_interactions(Iterator i, ConstIterator ci)
 | 
			
		||||
{
 | 
			
		||||
  bool eq = i == ci;               // comparisons
 | 
			
		||||
  bool ne = i != ci;            
 | 
			
		||||
  bool lt = i < ci;
 | 
			
		||||
  bool le = i <= ci;
 | 
			
		||||
  bool gt = i > ci;
 | 
			
		||||
  bool ge = i >= ci;
 | 
			
		||||
  std::size_t distance = i - ci;   // difference
 | 
			
		||||
  ci = i;                          // assignment
 | 
			
		||||
  ConstIterator ci2(i);            // construction
 | 
			
		||||
  convert<ConstIterator>(i);       // implicit conversion
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void f()
 | 
			
		||||
{
 | 
			
		||||
  typedef std::vector<int> vec;
 | 
			
		||||
  vec v;
 | 
			
		||||
  const vec& cv;
 | 
			
		||||
 | 
			
		||||
  test_interactions(v.begin(), cv.begin());   // <font color="#007F00">OK</font>
 | 
			
		||||
  test_interactions(v.rbegin(), cv.rbegin()); // <font color="#FF0000">ERRORS ON EVERY TEST!!</font>
 | 
			
		||||
</pre>
 | 
			
		||||
</blockquote>
 | 
			
		||||
Reverse iterators created with <tt>boost::reverse_iterator_generator</tt> don't have this problem, though:
 | 
			
		||||
<blockquote>
 | 
			
		||||
<pre>
 | 
			
		||||
  typedef boost::reverse_iterator_generator<vec::iterator>::type ri;
 | 
			
		||||
  typedef boost::reverse_iterator_generator<vec::const_iterator>::type cri;
 | 
			
		||||
  test_interactions(ri(v.begin()), cri(cv.begin()));   // <font color="#007F00">OK!!</font>
 | 
			
		||||
</pre>
 | 
			
		||||
</blockquote>
 | 
			
		||||
Or, more simply,
 | 
			
		||||
<blockquote>
 | 
			
		||||
<pre>
 | 
			
		||||
  test_interactions(
 | 
			
		||||
    boost::make_reverse_iterator(v.begin()), 
 | 
			
		||||
    boost::make_reverse_iterator(cv.begin()));   // <font color="#007F00">OK!!</font>
 | 
			
		||||
}
 | 
			
		||||
</pre>
 | 
			
		||||
</blockquote>
 | 
			
		||||
 | 
			
		||||
<p>If you are wondering why there is no
 | 
			
		||||
<tt>reverse_iterator_pair_generator</tt> in the manner of <tt><a
 | 
			
		||||
href="projection_iterator.htm#projection_iterator_pair_generator">projection_iterator_pair_generator</a></tt>,
 | 
			
		||||
the answer is simple: we tried it, but found that in practice it took
 | 
			
		||||
<i>more</i> typing to use <tt>reverse_iterator_pair_generator</tt> than to
 | 
			
		||||
simply use <tt>reverse_iterator_generator</tt> twice!<br><br>
 | 
			
		||||
 | 
			
		||||
<hr>
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    <p>Revised 
 | 
			
		||||
    <!--webbot bot="Timestamp" s-type="EDITED" s-format="%d %b %Y" startspan -->26 Feb 2001<!--webbot bot="Timestamp" endspan i-checksum="14386" -->
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    <p>© Copyright Jeremy Siek 2000. Permission to copy, use, modify, sell
 | 
			
		||||
    and distribute this document is granted provided this copyright notice
 | 
			
		||||
    appears in all copies. This document is provided "as is" without express or
 | 
			
		||||
    implied warranty, and with no claim as to its suitability for any purpose. 
 | 
			
		||||
    <!--  LocalWords:  html charset alt gif hpp BidirectionalIterator const namespace struct
 | 
			
		||||
         -->
 | 
			
		||||
     
 | 
			
		||||
    <!--  LocalWords:  ConstPointer ConstReference typename iostream int abcdefg
 | 
			
		||||
         -->
 | 
			
		||||
     <!--  LocalWords:  sizeof  PairGen pre Siek wroolllhed dehllloorw
 | 
			
		||||
         -->
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user