mirror of
https://github.com/boostorg/utility.git
synced 2025-05-09 15:04:00 +00:00
Initial commit
[SVN r7646]
This commit is contained in:
parent
b231894f1b
commit
ba62287576
489
c++_type_traits.htm
Normal file
489
c++_type_traits.htm
Normal file
@ -0,0 +1,489 @@
|
|||||||
|
<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 <…> 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>© Copyright John Maddock and Steve Cleary, 2000</p>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
Loading…
x
Reference in New Issue
Block a user