Section 2.5.  A Brief Tour of the Boost Type Traits Library
Team LiB
Previous Section Next Section

2.5. A Brief Tour of the Boost Type Traits Library

It 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. General

There 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 Categorization

These 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]

[8] Classes may be declared using the struct keyword, but they are still classes according to the C++ standard. In fact, the following two declarations are interchangeable:









class X;


struct X; // declares the same X





struct is only distinguished from class in definitions, where struct causes bases and members to be public by default.

There are two more categories that most programmers encounter less often. Pointers to member functions, which have the form

R (C::*)(args...) cv

and pointers to data members, written as

D C::*

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

R (args...)

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

R (*) (args...) or R (&) (args...)

Table 2.1 lists the primary type traits.

Table 2.1. Primary Type Categorization

Primary Trait

::type::value and ::value

is_array<T>

TRue iff T is an array type.

is_class<T>

true iff T is a class type; without compiler support, may also report TRue for unions.

is_enum<T>

TRue iff T is an enumeration type.

is_float<T>

TRue iff T is a floating-point type.

is_function<T>

true iff T is a function type.

is_integral<T>

true iff T is an integral type.

is_member_pointer<T>

true iff T is a pointer to function or data member.

is_pointer<T>

true iff T is a pointer type (but not a pointer to member).

is_reference<T>

TRue iff T is a reference type.

is_union<T>

true iff T is a union; without compiler support, always reports false.

is_void<T>

true iff T is of the form cv void.


2.5.3. Secondary Type Categorization

The traits in Table 2.2 represent useful groupings of, or distinctions within, the primary categories.

Table 2.2. Secondary Type Categorization

Secondary Trait

::type::value and ::value

is_arithmetic<T>

is_integral<T>::value ||
is_float<T>::value

is_compound<T>

!is_fundamental<T>::value

is_fundamental<T>

is_arithmetic<T>::value ||
is_void<T>::value

is_member_function_pointer<T>

true iff T is a pointer to member function.

is_object<T>

!(is_function<T>::value ||
is_reference<T>::value ||
is_void<T>::value)

is_scalar<T>

is_arithmetic<T>::value
|| is_enum<T>::value ||
is_pointer<T>::value ||
is_member_pointer<T>::value


2.5.4. Type Properties

The 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.

Table 2.3. Type Properties

Type Property

::type::value and ::value

alignment_of<T>

A positive multiple of T's memory alignment requirements (the library tries to minimize that multiple).

is_empty<T>

true iff the compiler optimizes away space for empty base classes and T is an empty class.

is_polymorphic<T>

true iff T is a class with at least one virtual member function.


Table 2.4. More Type Properties

Type Property

::type::value and ::value

has_nothrow_assign<T>

true only if T has a non-throwing assignment operator.

has_nothrow_constructor<T>

true only if T has a non-throwing default constructor.

has_nothrow_copy<T>

true only if T has a non-throwing copy constructor.

has_trivial_assign<T>

true only if T has a trivial assignment operator.

has_trivial_constructor<T>

true only if T has a trivial default constructor.

has_trivial_copy<T>

TRue only if T has a trivial copy constructor.

is_pod<T>

true only if T is a POD type.[9]

is_stateless<T>

true only if T is empty and its constructors and destructors are trivial.


[9] POD stands for "plain old data." Believe it or not, that's a technical term in the C++ standard. The standard gives us license to make all kinds of special assumptions about POD types. For example, PODs always occupy contiguous bytes of storage; other types might not. A POD type is defined to be either a scalar, an array of PODs, or a struct or union that has no user-declared constructors, no user-declared copy assignment, no user-declared destructor, no private or protected non-static data members, no base classes, no non-static data members of non-POD, reference, or pointer to member type, or array of such types, and no virtual functions.

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 Types

The 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 Transformations

The 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."

Table 2.5. Transformations Types

Transformation

::type

remove_const<T>

T without any top-level const. For example, const int becomes int, but const int* remains unchanged.

remove_volatile<T>

T without any top-level volatile. For example, volatile int becomes int.

remove_cv<T>

T without any top-level cv-qualifiers. For example, const volatile int becomes int.

remove_reference<T>

T without any top-level reference. For example, int& becomes int but int* remains unchanged.

remove_bounds<T>

T without any top-level array brackets. For example, int[2][3] becomes int[3].

remove_pointer<T>

T without any top-level pointer. For example, int* becomes int, but int& remains unchanged.

add_reference<T>

If T is a reference type, then T, otherwise T&.

add_pointer<T>

remove_reference<T>::type*. For example, int and int& both become int*.

add_const<T>

T const

add_volatile<T>

T volatile

add_cv<T>

T const volatile


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).

    Team LiB
    Previous Section Next Section