forked from boostorg/utility
Compare commits
133 Commits
svn-branch
...
svn-branch
Author | SHA1 | Date | |
---|---|---|---|
|
cd6e4b95a0 | ||
|
21efa95d5a | ||
|
a5adbbfd5f | ||
|
a19d13f123 | ||
|
78886ab383 | ||
|
168012b465 | ||
|
d9d58ea66e | ||
|
56f5f6e8d5 | ||
|
3cb6420eda | ||
|
60be2c1186 | ||
|
ed210f6b2c | ||
|
029bc59d74 | ||
|
961c08a82f | ||
|
7ee484c614 | ||
|
05c6fbbf99 | ||
|
91078b7f7a | ||
|
20d804afc4 | ||
|
c21f6d1cbf | ||
|
393e79c1fd | ||
|
8b92c8a085 | ||
|
ff73dd94c9 | ||
|
af43904f38 | ||
|
485074f265 | ||
|
2e0ee55b5e | ||
|
e9105d32cb | ||
|
964d23f68c | ||
|
be5aaaae7b | ||
|
bf13bd7b3f | ||
|
352e392fcb | ||
|
083b1b02df | ||
|
648c6240a2 | ||
|
60cab840cb | ||
|
83a4380dab | ||
|
de84fe8d98 | ||
|
ed3cbfdb8e | ||
|
fda44ca17d | ||
|
272025bb07 | ||
|
8e92bcf1b2 | ||
|
84f1ffdefe | ||
|
7e25450054 | ||
|
4a563fa266 | ||
|
aa4c0ec000 | ||
|
e1ecfbdc43 | ||
|
a4e122a82e | ||
|
93216e8fb7 | ||
|
16272c210d | ||
|
e104b00da1 | ||
|
ce5c6bcc08 | ||
|
8694ce31fe | ||
|
d960e5eadd | ||
|
2dc71e87a3 | ||
|
6bf17edde2 | ||
|
88573d515d | ||
|
89b9f77823 | ||
|
765d9be17d | ||
|
7135373008 | ||
|
ee269884fc | ||
|
387540d5f1 | ||
|
2eba7b42a8 | ||
|
07115d26c7 | ||
|
c43ed815a0 | ||
|
ff01e36d12 | ||
|
ac4798b16c | ||
|
d4e14fed0e | ||
|
5f91259344 | ||
|
20a9d9645d | ||
|
c86f6b4abd | ||
|
d66489b5b2 | ||
|
b743ee9f0c | ||
|
95ba69c00a | ||
|
2ac273739c | ||
|
5b4d28708c | ||
|
4cc4383488 | ||
|
8935232248 | ||
|
5c6dd2f172 | ||
|
eeeb7ef5b9 | ||
|
2efc9c1178 | ||
|
a84c46f6e3 | ||
|
a5c3dcdd02 | ||
|
46f7a75eb7 | ||
|
94b6710c5b | ||
|
d8dd3da9ab | ||
|
803ced004a | ||
|
0ea7d36ad0 | ||
|
87aafab759 | ||
|
994d310abd | ||
|
228cdcf05e | ||
|
42598e352c | ||
|
36a9e4d1da | ||
|
456dfd0dea | ||
|
155457e2b5 | ||
|
b5c91485bf | ||
|
c959cf7870 | ||
|
5878c88636 | ||
|
ddcef2fb19 | ||
|
493d124c07 | ||
|
f42060c616 | ||
|
834facc932 | ||
|
f82d0b76ee | ||
|
c25d225275 | ||
|
c503a274b5 | ||
|
087069d215 | ||
|
826a6dd114 | ||
|
f31483838d | ||
|
d8a9b633d9 | ||
|
c060e4466a | ||
|
a9951376f4 | ||
|
bda0c8f5e3 | ||
|
71902f23a2 | ||
|
dfd6c85569 | ||
|
0e41b2cc1a | ||
|
e5c81d0702 | ||
|
6caf7d4d5a | ||
|
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>
|
463
iterator_adaptor_test.cpp
Normal file
463
iterator_adaptor_test.cpp
Normal file
@@ -0,0 +1,463 @@
|
||||
// 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
|
||||
// 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&,
|
||||
std::forward_iterator_tag, std::ptrdiff_t, const dummyT*> 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&,
|
||||
std::input_iterator_tag, std::ptrdiff_t, const dummyT*> 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;
|
||||
}
|
Reference in New Issue
Block a user