]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/libs/type_traits/doc/background.qbk
add subtree-ish sources for 12.0.3
[ceph.git] / ceph / src / boost / libs / type_traits / doc / background.qbk
CommitLineData
7c673cae
FG
1[/
2 Copyright 2007 John Maddock.
3 Distributed under the Boost Software License, Version 1.0.
4 (See accompanying file LICENSE_1_0.txt or copy at
5 http://www.boost.org/LICENSE_1_0.txt).
6]
7
8[section:background Background and Tutorial]
9
10The following is an updated version of the article "C++ Type traits"
11by John Maddock and Steve Cleary that appeared in the October 2000
12issue of [@http://www.ddj.com Dr Dobb's Journal].
13
14Generic programming (writing code which works with any data type meeting a
15set of requirements) has become the method of choice for providing reusable code.
16However, there are times in generic programming when "generic" just isn't
17good enough - sometimes the differences between types are too large for an
18efficient generic implementation. This is when the traits technique
19becomes important - by encapsulating those properties that need to be
20considered on a type by type basis inside a traits class, we can
21minimize the amount of code that has to differ from one type to another,
22and maximize the amount of generic code.
23
24Consider an example: when working with character strings, one common operation is
25to determine the length of a null terminated string. Clearly it's possible to
26write generic code that can do this, but it turns out that there are much more
27efficient methods available: for example, the C library functions `strlen` and
28`wcslen` are usually written in assembler, and with suitable hardware support
29can be considerably faster than a generic version written in C++.
30The authors of the C++ standard library realized this, and abstracted the
31properties of `char` and `wchar_t` into the class `char_traits`. Generic code
32that works with character strings can simply use `char_traits<>::length` to
33determine the length of a null terminated string, safe in the knowledge
34that specializations of `char_traits` will use the most appropriate method
35available to them.
36
37[h4 Type Traits]
38
39Class `char_traits` is a classic example of a collection of type specific
40properties wrapped up in a single class - what Nathan Myers termed a
41/baggage class/[link background.references \[1\]]. In the Boost type-traits library, we[link background.references \[2\]] have written a
42set of very specific traits classes, each of which encapsulate a single trait
43from the C++ type system; for example, is a type a pointer or a reference type?
44Or does a type have a trivial constructor, or a const-qualifier?
45The type-traits classes share a unified design: each class inherits from
46the type __true_type if the type has the specified property and inherits from
47__false_type otherwise. As we will show, these classes can be used in
48generic programming to determine the properties of a given type and introduce
49optimizations that are appropriate for that case.
50
51The type-traits library also contains a set of classes that perform a
52specific transformation on a type; for example, they can remove a
53top-level const or volatile qualifier from a type. Each class that
54performs a transformation defines a single typedef-member `type`
55that is the result of the transformation. All of the type-traits
56classes are defined inside namespace `boost`; for brevity, namespace-qualification
57is omitted in most of the code samples given.
58
59[h4 Implementation]
60
61There are far too many separate classes contained in the type-traits library
62to give a full implementation here - see the source code in the Boost library
63for the full details - however, most of the implementation is fairly repetitive
64anyway, so here we will just give you a flavor for how some of the classes are
65implemented. Beginning with possibly the simplest class in the library,
66`is_void<T>` inherits from `__true_type` only if `T` is `void`.
67
68 template <typename T>
69 struct __is_void : public __false_type{};
70
71 template <>
72 struct __is_void<void> : public __true_type{};
73
74Here we define a primary version of the template class `__is_void`, and
75provide a full-specialization when `T` is `void`. While full specialization
76of a template class is an important technique, sometimes we need a
77solution that is halfway between a fully generic solution, and a full
78specialization. This is exactly the situation for which the standards committee
79defined partial template-class specialization. As an example, consider the
80class `boost::is_pointer<T>`: here we needed a primary version that handles
81all the cases where T is not a pointer, and a partial specialization to
82handle all the cases where T is a pointer:
83
84 template <typename T>
85 struct __is_pointer : public __false_type{};
86
87 template <typename T>
88 struct __is_pointer<T*> : public __true_type{};
89
90The syntax for partial specialization is somewhat arcane and could easily
91occupy an article in its own right; like full specialization, in order to
92write a partial specialization for a class, you must first declare the
93primary template. The partial specialization contains an extra <...> after the
94class name that contains the partial specialization parameters; these define
95the types that will bind to that partial specialization rather than the
96default template. The rules for what can appear in a partial specialization
97are somewhat convoluted, but as a rule of thumb if you can legally write two
98function overloads of the form:
99
100 void foo(T);
101 void foo(U);
102
103Then you can also write a partial specialization of the form:
104
105 template <typename T>
106 class c{ /*details*/ };
107
108 template <typename T>
109 class c<U>{ /*details*/ };
110
111This rule is by no means foolproof, but it is reasonably simple to remember
112and close enough to the actual rule to be useful for everyday use.
113
114As a more complex example of partial specialization consider the class
115`remove_extent<T>`. This class defines a single typedef-member `type` that
116is the same type as T but with any top-level array bounds removed;
117this is an example of a traits class that performs a transformation on a type:
118
119 template <typename T>
120 struct __remove_extent
121 { typedef T type; };
122
123 template <typename T, std::size_t N>
124 struct __remove_extent<T[N]>
125 { typedef T type; };
126
127The aim of `__remove_extent` is this: imagine a generic algorithm that is
128passed an array type as a template parameter, `__remove_extent` provides a
129means of determining the underlying type of the array. For example
130`remove_extent<int[4][5]>::type` would evaluate to the type `int[5]`.
131This example also shows that the number of template parameters in a
132partial specialization does not have to match the number in the
133default template. However, the number of parameters that appear after the
134class name do have to match the number and type of the parameters in the
135default template.
136
137[h4 Optimized copy]
138
139As an example of how the type traits classes can be used, consider the
140standard library algorithm copy:
141
142 template<typename Iter1, typename Iter2>
143 Iter2 copy(Iter1 first, Iter1 last, Iter2 out);
144
145Obviously, there's no problem writing a generic version of copy that works
146for all iterator types `Iter1` and `Iter2`; however, there are some
147circumstances when the copy operation can best be performed by a call to
148`memcpy`. In order to implement copy in terms of `memcpy` all of the
149following conditions need to be met:
150
151* Both of the iterator types `Iter1` and `Iter2` must be pointers.
152* Both `Iter1` and `Iter2` must point to the same type - excluding const and
153volatile-qualifiers.
154* The type pointed to by `Iter1` must have a trivial assignment operator.
155
156By trivial assignment operator we mean that the type is either a scalar type[link background.references \[3\]] or:
157
158* The type has no user defined assignment operator.
159* The type does not have any data members that are references.
160* All base classes, and all data member objects must have trivial assignment operators.
161
162If all these conditions are met then a type can be copied using `memcpy`
163rather than using a compiler generated assignment operator. The type-traits
164library provides a class `__has_trivial_assign`, such that
165`has_trivial_assign<T>::value` is true only if T has a trivial assignment operator.
166This class "just works" for scalar types, but has to be explicitly
167specialised for class/struct types that also happen to have a trivial assignment
168operator. In other words if __has_trivial_assign gives the wrong answer,
169it will give the "safe" wrong answer - that trivial assignment is not allowable.
170
171The code for an optimized version of copy that uses `memcpy` where appropriate is
172given in [link boost_typetraits.examples.copy the examples]. The code begins by defining a template
173function `do_copy` that performs a "slow but safe" copy. The last parameter passed
174to this function may be either a `__true_type` or a `__false_type`. Following that
175there is an overload of do_copy that uses `memcpy`: this time the iterators are required
176to actually be pointers to the same type, and the final parameter must be a
177`__true_type`. Finally, the version of `copy` calls `do_copy`, passing
178`__has_trivial_assign<value_type>()` as the final parameter: this will dispatch
179to the optimized version where appropriate, otherwise it will call the
180"slow but safe version".
181
182[h4 Was it worth it?]
183
184It has often been repeated in these columns that "premature optimization is the
185root of all evil" [link background.references \[4\]]. So the question must be asked: was our optimization
186premature? To put this in perspective the timings for our version of copy
187compared a conventional generic copy[link background.references \[5\]] are shown in table 1.
188
189Clearly the optimization makes a difference in this case; but, to be fair,
190the timings are loaded to exclude cache miss effects - without this
191accurate comparison between algorithms becomes difficult. However, perhaps
192we can add a couple of caveats to the premature optimization rule:
193
194*If you use the right algorithm for the job in the first place then optimization
195will not be required; in some cases, memcpy is the right algorithm.
196*If a component is going to be reused in many places by many people then
197optimizations may well be worthwhile where they would not be so for a single
198case - in other words, the likelihood that the optimization will be
199absolutely necessary somewhere, sometime is that much higher.
200Just as importantly the perceived value of the stock implementation will be
201higher: there is no point standardizing an algorithm if users reject it on
202the grounds that there are better, more heavily optimized versions available.
203
204[table Time taken to copy 1000 elements using `copy<const T*, T*>` (times in micro-seconds)
205
206[[Version] [T] [Time]]
207[["Optimized" copy] [char] [0.99]]
208[[Conventional copy] [char] [8.07]]
209[["Optimized" copy] [int] [2.52]]
210[[Conventional copy] [int] [8.02]]
211]
212
213[h4 Pair of References]
214The optimized copy example shows how type traits may be used to perform
215optimization decisions at compile-time. Another important usage of type traits
216is to allow code to compile that otherwise would not do so unless excessive
217partial specialization is used. This is possible by delegating partial
218specialization to the type traits classes. Our example for this form of
219usage is a pair that can hold references [link background.references \[6\]].
220
221First, let us examine the definition of `std::pair`, omitting the
222comparison operators, default constructor, and template copy constructor for
223simplicity:
224
225 template <typename T1, typename T2>
226 struct pair
227 {
228 typedef T1 first_type;
229 typedef T2 second_type;
230
231 T1 first;
232 T2 second;
233
234 pair(const T1 & nfirst, const T2 & nsecond)
235 :first(nfirst), second(nsecond) { }
236 };
237
238Now, this "pair" cannot hold references as it currently stands, because the
239constructor would require taking a reference to a reference, which is
240currently illegal [link background.references \[7\]]. Let us consider what the constructor's parameters
241would have to be in order to allow "pair" to hold non-reference types,
242references, and constant references:
243
244[table Required Constructor Argument Types
245[[Type of `T1`] [Type of parameter to initializing constructor]]
246[[T] [const T &]]
247[[T &] [T &]]
248[[const T &] [const T &]]
249]
250
251A little familiarity with the type traits classes allows us to construct a
252single mapping that allows us to determine the type of parameter from the
253type of the contained class. The type traits classes provide a
254transformation __add_reference, which adds a reference to its type,
255unless it is already a reference.
256
257[table Using add_reference to synthesize the correct constructor type
258[[Type of `T1`] [Type of `const T1`] [Type of `add_reference<const T1>::type`]]
259[[T] [const T] [const T &]]
260[[T &] [T & \[8\]] [T &]]
261[[const T &] [const T &] [const T &]]
262]
263
264This allows us to build a primary template definition for `pair` that can
265contain non-reference types, reference types, and constant reference types:
266
267 template <typename T1, typename T2>
268 struct pair
269 {
270 typedef T1 first_type;
271 typedef T2 second_type;
272
273 T1 first;
274 T2 second;
275
276 pair(boost::__add_reference<const T1>::type nfirst,
277 boost::__add_reference<const T2>::type nsecond)
278 :first(nfirst), second(nsecond) { }
279 };
280
281Add back in the standard comparison operators, default constructor,
282and template copy constructor (which are all the same), and you have a
283`std::pair` that can hold reference types!
284
285This same extension could have been done using partial template specialization
286of `pair`, but to specialize `pair` in this way would require three partial
287specializations, plus the primary template. Type traits allows us to
288define a single primary template that adjusts itself auto-magically to
289any of these partial specializations, instead of a brute-force partial
290specialization approach. Using type traits in this fashion allows
291programmers to delegate partial specialization to the type traits classes,
292resulting in code that is easier to maintain and easier to understand.
293
294[h4 Conclusion]
295
296We hope that in this article we have been able to give you some idea of
297what type-traits are all about. A more complete listing of the available
298classes are in the boost documentation, along with further examples using
299type traits. Templates have enabled C++ uses to take the advantage of the
300code reuse that generic programming brings; hopefully this article has
301shown that generic programming does not have to sink to the lowest common
302denominator, and that templates can be optimal as well as generic.
303
304[h4 Acknowledgements]
305
306The authors would like to thank Beman Dawes and Howard Hinnant for their
307helpful comments when preparing this article.
308
309[h4 [#background.references]References]
310
311# Nathan C. Myers, C++ Report, June 1995.
312# 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.
313# 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.
314# This quote is from Donald Knuth, ACM Computing Surveys, December 1974, pg 268.
315# 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.
316# 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".
317# 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.
318# 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.
319
320[endsect]
321
322