Section 2.9.  Details
Team LiB
Previous Section Next Section

2.9. Details

We've covered a lot of ground in this chapter; the journey from traits to metafunctions takes us from the ad hoc type associations used in the simplest generic programs, to the fundamentals that allow metaprogramming to be viewed as a first-class coding activity. We also dipped into the mechanics of C++ templates, got an overview of the type traits library, and saw some of its components in action. In such a broad landscape, some important details are bound to be missed; we'll fill them in as we review the chapter's most important points.

Specialization

The meaning of specialization as applied to C++ templates can be tough to get a handle on, because it's used in two different ways. Its first usage refers to a template with specific arguments filled in, as in iterator_traits<int*>. In other words, a template specialization names the actual class (or function, in the case of function template specializations) that results from replacing its parameters with specific arguments.

The second use of specialization shows up in "explicit specialization" and "partial specialization"; we showed both explicit and partial specializations of iterator_traits in this chapter. The name "explicit" is probably not well-chosen, since partial specializations are just as explicit; you can think of "explicit specialization" as meaning "full specialization" without any loss of understanding.

To remember the syntax rules for declaring class template specializations (the second meaning), keep this form in mind:



    template <variable part>


    struct template-name<fixed part>



In an explicit (or full) specialization, the variable part is empty and the fixed part consists of concrete template arguments. In a partial specialization, the variable part contains a parameter list, and at least one argument in the fixed part depends on those parameters.

Primary template

The declaration of a template that is not a specialization (second meaning above) is called the primary template. We can think of the primary template as covering the general case, while specializations cover various special cases.

Instantiation

The moment the compiler needs to know much more about a template than what its arguments arethe names of its members or the identity of its base classes, for examplethe template will be instantiated. At that point, the compiler fills in the actual values of all the template parameters, picks the best-matching explicit or partial specialization (if any), figures out all of the types and constants used in declarations in the template body, and rechecks those declarations for errors. It does not, however, instantiate definitions (such as member function bodies) until they are actually used. For example:



    template <class T, class U>


    struct X


    {


        int f(T* x) // declaration


        {


           U y[10]; // definition


           return 0;


        }


    };





    typedef X<int&, char> t1; // OK; no instantiation yet


    t1 x1;                    // error: pointer to int& illegal


    typedef X<int, char&> t2;


    t2 x2;                    // OK; declarations check out


    int a = x2.f();           // error: array of char& illegal



As you can see, template instantiation can affect not only compilation speed, but whether your program compiles at all!

The blob

A class with lots of members (including member functions) is known in the object-oriented programming literature as a "blob" [BMMM98]. The members of a class are all "coupled" to one another, because they must be declared together. To avoid coupling and increase modularity, avoid this anti-pattern. Instead, define separate traits as independent metafunctions.

Metadata

A "value" that can be manipulated by the C++ compile-time machinery can be thought of as metadata. In template metaprogramming, the two most common kinds of metadata are types and integer (including bool) constants. The compile-time part of C++ is often referred to as a "pure functional language" because metadata is immutable and metafunctions can't have any side effects.

Polymorphism

Literally, "having many forms." In computer languages polymorphism has come to mean the ability to manipulate different types through a common interface. Having a consistent interface is the best way to ensure that code is reusable and components interoperate naturally. Because C++ templates do not inherently treat the different kinds of metadata polymorphically, MPL follows the convention of using type wrappers around non-type metadata. In particular, numerical metadata is represented as a type with a nested numeric constant member called ::value.

Metafunction

A "function" that operates on metadata and can be "invoked" at compile time. For the remainder of this book, a template or class will only be called a metafunction if it has no non-type parameters and returns a type called type. The arguments to the class template are the inputs to the compile time computation, and the ::type member is its result. Thus, expressions like:



    some_metafunction<Arg1, Arg2>::type



are analogous to runtime computations like:



    some_function(arg1, arg2)



Numerical metafunction

A metafunction that returns a wrapper type for a numerical value. As a convenience, many numerical metafunctions themselves provide a nested ::value member, so that you can write:



    some_numerical_metafunction<Arg>::value



instead of the more general:



    some_numerical_metafunction<Arg>::type::value



if you want to access the numerical result directly.

Nullary metafunction

Any class with a publicly accessible ::type can be used as a metafunction that accepts no arguments. As a consequence of this definition, any metafunction specialization (first meaning above), such as boost::remove_pointer<char*>, is a legal nullary metafunction.

Traits

A technique for establishing associations between pieces of metadata via class template specializations. A key feature of the traits idiom is that it's non-intrusive: We can establish a new mapping without modifying the associated items themselves. MPL metafunctions can be viewed as a special case of traits, where there is only one result value for any input.

Type traits

The Boost Type Traits library is a metafunction library. It contains a few metafunctions for low-level type manipulation. For example, the result of add_reference is always a reference type:



    boost::add_reference<char>::type      // char&


    boost::add_reference<int&>::type      // int&



The Type Traits library is mainly comprised of Boolean-valued metafunctions that can be used to answer questions about the fundamental properties of any type. For example:



    boost::is_reference<char>::value      // false


    boost::is_reference<int&>::value      // true



    Team LiB
    Previous Section Next Section