2.5. A Brief Tour of the Boost Type Traits LibraryIt turns out that almost every serious template metaprogram ends up needing facilities like those provided by the Boost. Type Traits. The library has proven so useful that it has been accepted into the C++ standard committee's first "Technical Report" ([n1424], [n1519]), a harbinger of things to come in the next official standard. For a complete reference, see the documentation in the libs/type_traits subdirectory of your Boost distribution, or at http://www.boost.org/libs/type_traits. 2.5.1. GeneralThere are a few things you need to know about the library as a whole: First, as you may have guessed from the iter_swap example, all of the library's metafunctions are in namespace boost, and there's a simple convention for #include-ing the header that defines any of them:
#include <boost/type_traits/ metafunction-name.hpp>
Second, as we implied earlier, all of Boost's numerical metafunctions, as a convenience, provide a nested ::value directly in the metafunction. It may be a bit strange to think of bool-valued metafunctions like is_reference as "numerical," but C++ classifies bool as an integral type and the same convention applies to all integral-valued metafunctions. Third, there are a few type traits (e.g., has_trivial_destructor) that require non-standard compiler support in order to be fully functional. A few compilers, notably Metrowerks CodeWarrior and SGI MipsPro, have actually implemented the necessary primitives. On other compilers, these traits generally are correct for some types and degrade "gracefully and safely" for others. By "gracefully" we mean that even when the traits don't have the correct result, their use will still compile. To understand what we mean by "safely," you have to know that these traits are mostly used to decide whether certain optimizations are possible. For example, the storage for a type with a trivial destructor may be reused without destroying the object it contains. If, however, you can't determine that the type has a trivial destructor, you must destroy the object before reusing its storage. When has_trivial_destructor<T> can't determine the correct result value, it returns false so that generic code will always take the safe route and invoke T's destructor. Last, be aware that type categorization metafunctions like is_enum<T>, which we describe next, generally ignore cv-qualification (const and volatile), so that is_enum<T const> always has the same result as is_enum<T>. Each of the following subsections describes a different group of traits. 2.5.2. Primary Type CategorizationThese unary metafunctions determine which of the fundamental type categories described in the C++ standard applies to their argument. For any given type T, one and only one of these metafunctions should yield a TRue result. Nine traits cover the type categories that most people are familiar with. There's not much to say about is_void<T>, is_pointer<T>, is_reference<T>, is_array<T>, and is_enum<T>; they do just what you'd expect. is_integral<T> identifies char, bool, and all the varieties of signed and unsigned integer types. Similarly, is_float<T> identifies the floating-point types float, double, and long double. Unfortunately, without compiler support, is_union<T> always returns false, thus is_class<T> is true for both classes and unions.[8]
class X; struct X; // declares the same X
There are two more categories that most programmers encounter less often. Pointers to member functions, which have the form
and pointers to data members, written as
are identified by is_member_pointer<T>. Note that is_pointer doesn't identify these types, even though they're called pointers. Lastly, is_function<T> identifies function types of the form
Many people never see an unadorned function type, because of the way function names, when not immediately invoked, automatically become function pointers or references of the form
Table 2.1 lists the primary type traits.
2.5.3. Secondary Type CategorizationThe traits in Table 2.2 represent useful groupings of, or distinctions within, the primary categories.
2.5.4. Type PropertiesThe type traits library uses the term properties as a kind of catch-all term for traits other than those directly related to the standard type categories. The simplest traits in this group are is_const and is_volatile, which detect their arguments' cv-qualification. The rest of the type properties are summarized in Tables 2.3 and 2.4.
The traits in Table 2.4 are most useful for selecting optimizations. With compiler support they can be implemented more accurately, allowing only if to be replaced by if and only if (iff) in the table. 2.5.5. Relationships Between TypesThe library contains three important metafunctions that indicate relationships between types. We've already seen is_same<T,U>, whose ::value is TRue when T and U are identical types. is_convertible<T,U> yields true if and only if an object of type T can be implicitly converted to type U. Finally, is_base_and_derived<B,D>::value is true if and only if B is a base class of D. 2.5.6. Type TransformationsThe metafunctions listed in Table 2.5 perform fundamental type manipulations. Note that unlike other type traits metafunctions we've discussed so far, members of this group yield type results rather than Boolean values. You can think of them as being operators in the "arithmetic of types."
At this point you might be wondering why we bothered with the last three transformations in the table. After all, add_const<T>::type is just a more verbose way to write T const. It turns out that it's important to be able to express even the simplest transformations in metafunction form so they can be passed on to other metafunctions (which, as promised, we'll show you how to do in the next chapter). |