C++ Templates: The Complete Guide Traits and Policy Classes

Traits and Policy Classes

Traits and Policy Classes

Templates enable us to parameterize classes and functions for various types. It could be tempting to introduce as many template parameters as possible to enable the customization of every aspect of a type or algorithm. In this way, our "templatized" components could be instantiated to meet the exact needs of client code.
However, from a practical point of view it is rarely desirable to introduce dozens of template parameters for maximal parameterization. Having to specify all the corresponding arguments in the client code is overly tedious.

Fortunately, it turns out that most of the extra parameters we would introduce have reasonable default values. In some cases the extra parameters are entirely determined by a few main parameters, and we'll see that such extra parameters can be omitted altogether. Other parameters can be given default values that depend on the main parameters and will meet the needs of most situations, but the default values must occasionally be overridden (for special applications). Yet other parameters are unrelated to the main parameters: In a sense they are themselves main parameters, except for the fact that there exist default values that almost always fit the bill.

Policy classes and traits (or traits templates) are C++ programming devices that greatly facilitate the management of the sort of extra parameters that come up in the design of industrial-strength templates. In this chapter we show a number of situations in which they prove useful and demonstrate various techniques that will enable you to write robust and powerful devices of your own.

An Example: Accumulating a Sequence

Computing the sum of a sequence of values is a fairly common computational task. However, this seemingly simple problem provides us with an excellent example to introduce various levels at which policy classes and traits can help.

15.1.1 Fixed Traits

Let's first assume that the values of the sum we want to compute are stored in an array, and we are given a pointer to the first element to be accumulated and a pointer one past the last element to be accumulated. Because this book is about templates, we wish to write a template that will work for many types. The following may seem straightforward by now [1]:
[1] Most examples in this section use ordinary pointers for the sake of simplicity. Clearly, an industrial-strength interface may prefer to use iterator parameters following the conventions of the C++ standard library (see [JosuttisStdLib]). We revisit this aspect of our example later.
// traits/accum1.hpp 

#ifndef ACCUM_HPP 
#define ACCUM_HPP 

template <typename T> 
inline 
T accum (T const* beg, T const* end) 
{ 
    T total = T();  // assume T() actually creates a zero value 
    while (beg != end) { 
        total += *beg; 
        ++beg; 
    } 
    return total; 
} 

#endif // ACCUM_HPP 
The only slightly subtle decision here is how to create a zero value of the correct type to start our summation. We use the expression T() here, which normally should work for built-in numeric types like int and float (see Section 5.5 on page 56).
To motivate our first traits template, consider the following code that makes use of our accum():
// traits/accum1.cpp 

#include "accum1.hpp" 
#include <iostream> 

int main() 
{ 

// create array of 5 integer values 
int num[]={1,2,3,4,5}; 

// print average value 
std::cout << "the average value of the integer values is " 
          << accum(&num[0], &num[5]) / 5 
          << '\n'; 

// create array of character values 
char name[] = "templates"; 
int length = sizeof(name)-1; 

// (try to) print average character value 
std::cout << "the average value of the characters in \"" 
          << name << "\" is " 
          << accum(&name[0], &name[length]) / length 
          << '\n'; 
} 
In the first half of the program we use accum() to sum five integer values:
int num[]={1,2,3,4,5}; 
 
accum(&num[0], &num[5]) 
The average integer value is then obtained by simply dividing the resulting sum by the number of values in the array.
The second half of the program attempts to do the same for all letters in the word template (provided the characters from a to z form a contiguous sequence in the actual character set, which is true for ASCII but not for EBCDIC [2]). The result should presumably lie between the value of a and the value of z. On most platforms today, these values are determined by the ASCII codes: a is encoded as 97 and z is encoded as 122. Hence, we may expect a result between 97 and 122. However, on our platform the output of the program is as follows:
[2] EBCDIC is an abbreviation of Extended Binary-Coded Decimal Interchange Code, which is an IBM character set that is widely used on large IBM computers.
the average value of the integer values is 3 
the average value of the characters in "templates" is -5 
The problem here is that our template was instantiated for the type char, which turns out to be too by introducing an additional template parameter AccT that describes the type used for the variable total (and hence the return type). However, this would put an extra burden on all users of our template: They would have to specify an extra type in every invocation of our template. In our example we may, therefore, need to write the following:
accum<int>(&name[0],&name[length]) 
This is not an excessive constraint, but it can be avoided.
An alternative approach to the extra parameter is to create an association between each type T for which accum() is called and the corresponding type that should be used to hold the accumulated value. This association could be considered characteristic of the type T, and therefore the type in which the sum is computed is sometimes called a trait of T. As is turns out, our association can be encoded as specializations of a template:
// traits/accumtraits2.hpp 

template<typename T> 
class AccumulationTraits; 

template<> 
class AccumulationTraits<char> { 
  public: 
    typedef int AccT; 
}; 

template<> 
class AccumulationTraits<short> { 
  public: 
    typedef int AccT; 
}; 

template<> 
class AccumulationTraits<int> { 
  public: 
    typedef long AccT; 
}; 

template<> 
class AccumulationTraits<unsigned int> { 
  public: 
    typedef unsigned long AccT; 
}; 

template<> 
class AccumulationTraits<float> { 
  public: 
    typedef double AccT; 
}; 
The template AccumulationTraits is called a traits template because it holds a trait of its parameter type. (In general, there could be more than one trait and more than one parameter.) We chose not to provide a generic definition of this template because there isn't a great way to select a good accumulation type when we don't know what the type is. However, an argument could be made that T itself is often a good candidate for such a type (although clearly not in our earlier example).
With this in mind, we can rewrite our accum() template as follows:
// traits/accum2.hpp 

#ifndef ACCUM_HPP 
#define ACCUM_HPP 

#include "accumtraits2.hpp" 

template <typename T> 
inline 
typename AccumulationTraits<T>::AccT accum (T const* beg, 
                                            T const* end) 
{ 
    // return type is traits of the element type 
    typedef typename AccumulationTraits<T>::AccT AccT; 

    AccT total = AccT();  // assume T() actually creates a zero value 
    while (beg != end) { 
        total += *beg; 
        ++beg; 
    } 
    return total; 
} 

#endif // ACCUM_HPP 
The output of our sample program then becomes what we expect:
the average value of the integer values is 3 
the average value of the characters in "templates" is 108 
Overall, the changes aren't very dramatic considering that we have added a very useful mechanism to customize our algorithm. Furthermore, if new types arise for use with accum(), an appropriate AccT can be associated with it simply by declaring an additional explicit specialization of the AccumulationTraits template. Note that this can be done for any type: fundamental types, types that are declared in other libraries, and so forth.

15.1.2 Value Traits

So far, we have seen that traits represent additional type information related to a given "main" type. In this section we show that this extra information need not be limited to types. Constants and other classes of values can be associated with a type as well.
Our original accum() template uses the default constructor of the return value to initialize the result variable with what is hoped to be a zero-like value:
AccT total = AccT();  // assume T() actually creates a zero value 
 
return total; 
Clearly, there is no guarantee that this produces a good value to start the accumulation loop. Type T may not even have a default constructor.
Again, traits can come to the rescue. For our example, we can add a new value trait to our AccumulationTraits:
// traits/accumtraits3.hpp 

template<typename T> 
class AccumulationTraits; 

template<> 
class AccumulationTraits<char> { 
  public: 
    typedef int AccT; 
    static AccT const zero = 0; 
}; 

template<> 
class AccumulationTraits<short> { 
  public: 
    typedef int AccT; 
    static AccT const zero = 0; 
}; 

template<> 
class AccumulationTraits<int> { 
  public: 
    typedef long AccT; 
    static AccT const zero = 0; 
}; 
 
In this case, our new trait is a constant that can be evaluated at compile time. Thus, accum() becomes:
// traits/accum3.hpp 

#ifndef ACCUM_HPP 
#define ACCUM_HPP 

#include "accumtraits3.hpp" 

template <typename T> 
inline 
typename AccumulationTraits<T>::AccT accum (T const* beg, 
                                            T const* end) 
{ 
    // return type is traits of the element type 
    typedef typename AccumulationTraits<T>::AccT AccT; 

    AccT total = AccumulationTraits<T>::zero; 
    while (beg != end) { 
        total += *beg; 
        ++beg; 
    } 
    return total; 
} 

#endif // ACCUM_HPP 
In this code, the initialization of the accumulation variable remains straightforward:
AccT total = AccumulationTraits<T>::zero; 
A drawback of this formulation is that C++ allows us to initialize only a static constant data member inside its class if it has an integral or enumeration type. This excludes our own classes, of course, and floating-point types as well. The following specialization is, therefore, an error:
 
template<> 
class AccumulationTraits<float> { 
public: 
    typedef double AccT; 
    static double const zero = 0.0;  // ERROR: not an integral type 
}; 
The straightforward alternative is not to define the value trait in its class:
 
template<> 
class AccumulationTraits<float> { 
public: 
    typedef double AccT; 
    static double const zero; 
}; 
The initializer then goes in a source file and looks something like the following:
 
double const AccumulationTraits<float>::zero = 0.0; 
Although this works, it has the disadvantage of being more opaque to compilers. While processing client files, compilers are typically unaware of definitions in other files. In this case, for example, a compiler would not be able to take advantage of the fact that the value zero is really 0.0.
Consequently, we prefer to implement value traits, which are not guaranteed to have integral values as inline member functions. [3] For example, we could rewrite AccumulationTraits as follows:
[3] Most modern C++ compilers can "see through" calls of simple inline functions.
// traits/accumtraits4.hpp 

template<typename T> 
class AccumulationTraits; 

template<> 
class AccumulationTraits<char> { 
  public: 
    typedef int AccT; 
    static AccT zero() { 
        return 0; 
    } 
}; 

template<> 
class AccumulationTraits<short> { 
  public: 
    typedef int AccT; 
    static AccT zero() { 
        return 0; 
    } 
}; 

template<> 
class AccumulationTraits<int> { 
  public: 
    typedef long AccT; 
    static AccT zero() { 
        return 0; 
    } 
}; 

template<> 
class AccumulationTraits<unsigned int> { 
  public: 
    typedef unsigned long AccT; 
    static AccT zero() { 
        return 0; 
    } 
}; 

template<> 
class AccumulationTraits<float> { 
  public: 
    typedef double AccT; 
    static AccT zero() { 
        return 0; 
    } 
}; 
 
For the application code, the only difference is the use of function call syntax (instead of the slightly more concise access to a static data member):
AccT total = AccumulationTraits<T>::zero(); 
Clearly, traits can be more than just extra types. In our example, they can be a mechanism to provide all the necessary information that accum() needs about the element type for which it is called. This is the key of the traits concept: Traits provide an avenue to configure concrete elements (mostly types) for generic computations.

15.1.3 Parameterized Traits

The use of traits in accum() in the previous sections is called fixed, because once the decoupled trait is defined, it cannot be overridden in the algorithm. There may be cases when such overriding is desirable. For example, we may happen to know that a set of float values can safely be summed into a variable of the same type, and doing so may buy us some efficiency.
In principle, the solution consists of adding a template parameter but with a default value determined by our traits template. In this way, many users can omit the extra template argument, but those with more exceptional needs can override the preset accumulation type. The only bee in our bonnet for this particular case is that function templates cannot have default template arguments. [4]
[4] This is almost certainly going to change in a revision of the C++ standard, and compiler vendors are likely to provide the feature even before this revised standard is published (see Section 13.3 on page 207).
For now, let's circumvent the problem by formulating our algorithm as a class. This also illustrates the fact that traits can be used in class templates at least as easily as in function templates. The drawback in our application is that class templates cannot have their template arguments deduced. They must be provided explicitly. Hence, we need the form
Accum<char>::accum(&name[0], &name[length]) 
to use our revised accumulation template:
// traits/accum5.hpp 

#ifndef ACCUM_HPP 
#define ACCUM_HPP 

#include "accumtraits4.hpp" 

template <typename T, 
          typename AT = AccumulationTraits<T> > 
class Accum { 
  public: 
    static typename AT::AccT accum (T const* beg, T const* end) { 
        typename AT::AccT total = AT::zero(); 
        while (beg != end) { 
            total += *beg; 
            ++beg; 
        } 
        return total; 
    } 
}; 

#endif // ACCUM_HPP 
Presumably, most users of this template would never have to provide the second template argument explicitly because it can be configured to an appropriate default for every type used as a first argument.
As is often the case, we can introduce convenience functions to simplify the interface:
template <typename T> 
inline 
typename AccumulationTraits<T>::AccT accum (T const* beg, 
                                            T const* end) 
{ 
    return Accum<T>::accum(beg, end); 
} 

template <typename Traits, typename T> 
inline 
typename Traits::AccT accum (T const* beg, T const* end) 
{ 
    return Accum<T, Traits>::accum(beg, end); 
} 

15.1.4 Policies and Policy Classes

So far we have equated accumulation with summation. Clearly we can imagine other kinds of accumulations. For example, we could multiply the sequence of given values. Or, if the values were strings, we could concatenate them. Even finding the maximum value in a sequence could be formulated as an accumulation problem. In all these alternatives, the only accum() operation that needs to change is total += *start. This operation can be called a policy of our accumulation process. A policy class, then, is a class that provides an interface to apply one or more policies in an algorithm. [5]
[5] We could generalize this to a policy parameter, which could be a class (as discussed) or a pointer to a function.
Here is an example of how we could introduce such an interface in our Accum class template:
// traits/accum6.hpp 

#ifndef ACCUM_HPP 
#define ACCUM_HPP 

#include "accumtraits4.hpp" 
#include "sumpolicy1.hpp" 

template <typename T, 
          typename Policy = SumPolicy, 
          typename Traits = AccumulationTraits<T> > 
class Accum { 
  public: 
    typedef typename Traits::AccT AccT; 
    static AccT accum (T const* beg, T const* end) { 
        AccT total = Traits::zero(); 
        while (beg != end) { 
            Policy::accumulate(total, *beg); 
            ++beg; 
        } 
        return total; 
    } 
}; 

#endif // ACCUM_HPP 
With this a SumPolicy could be written as follows:
// traits/sumpolicy1.hpp 

#ifndef SUMPOLICY_HPP 
#define SUMPOLICY_HPP 

class SumPolicy { 
  public: 
    template<typename T1, typename T2> 
    static void accumulate (T1& total, T2 const & value) { 
        total += value; 
    } 
}; 

#endif // SUMPOLICY_HPP 
In this example we chose to make our policy an ordinary class (that is, not a template) with a static member function template (which is implicitly inline). We discuss an alternative option later.
By specifying a different policy to accumulate values we can compute different things. Consider, for example, the following program, which intends to determine the product of some values:
// traits/accum7.cpp 

#include "accum6.hpp" 
#include <iostream> 

class MultPolicy { 
  public: 
    template<typename T1, typename T2> 
    static void accumulate (T1& total, T2 const& value) { 
        total *= value; 
    } 
}; 

int main() 
{ 
    // create array of 5 integer values 
    int num[]={1,2,3,4,5}; 

    // print product of all values 
    std::cout << "the product of the integer values is " 
              << Accum<int,MultPolicy>::accum(&num[0], &num[5]) 
              << '\n'; 
} 
However, the output of this program isn't what we would like:
the product of the integer values is 0 
The problem here is caused by our choice of initial value: Although 0 works well for summation, it does not work for multiplication (a zero initial value forces a zero result for accumulated multiplications). This illustrates that different traits and policies may interact, underscoring the importance of careful template design.
In this case we may recognize that the initialization of an accumulation loop is a part of the accumulation policy. This policy may or may not make use of the trait zero(). Other alternatives are not to be forgotten: Not everything must be solved with traits and policies. For example, the accumulate() function of the C++ standard library takes the initial value as a third (function call) argument.

15.1.5 Traits and Policies: What's the Difference?

A reasonable case can be made in support of the fact that policies are just a special case of traits. Conversely, it could be claimed that traits just encode a policy.
The New Shorter Oxford English Dictionary (see [NewShorterOED]) has this to say:
  • trait n. ... a distinctive feature characterizing a thing
  • policy n. ... any course of action adopted as advantegous or expedient
Based on this, we tend to limit the use of the term policy classes to classes that encode an action of some sort that is largely orthogonal with respect to any other template argument with which it is combined. This is in agreement with Andrei Alexandrescu's statement in his book Modern C++ Design (see page 8 of [AlexandrescuDesign]) [6]:
[6] Alexandrescu has been the main voice in the world of policy classes, and he has developed a rich set of techniques based on them.
Policies have much in common with traits but differ in that they put less emphasis on type and more on behavior.
Nathan Myers, who introduced the traits technique, proposed the following more open-ended definition (see [MyersTraits]):
Traits class: A class used in place of template parameters. As a class, it aggregates useful types and constants; as a template, it provides an avenue for that "extra level of indirection" that solves all software problems.
In general, we therefore tend to use the following (slightly fuzzy) definitions:
  • Traits represent natural additional properties of a template parameter.
  • Policies represent configurable behavior for generic functions and types (often with some commonly used defaults).
To elaborate further on the possible distinctions between the two concepts, we list the following observations about traits:
  • Traits can be useful as fixed traits (that is, without being passed through template parameters).
  • Traits parameters usually have very natural default values (which are rarely overridden, or simply cannot be overridden).
  • Traits parameters tend to depend tightly on one or more main parameters.
  • Traits mostly combine types and constants rather than member functions.
  • Traits tend to be collected in traits templates.
For policy classes, we make the following observations:
  • Policy classes don't contribute much if they aren't passed as template parameters.
  • Policy parameters need not have default values and are often specified explicitly (although many generic components are configured with commonly used default policies).
  • Policy parameters are mostly orthogonal to other parameters of a template.
  • Policy classes mostly combine member functions.
  • Policies can be collected in plain classes or in class templates.
However, there is certainly an indistinct line between both terms. For example, the character traits of the C++ standard library also define functional behavior such as comparing, moving, and finding characters. And by replacing these traits you can define string classes that behave in a case-insensitive manner (see Section 11.2.14 in [JosuttisStdLib]) while keeping the same character type. Thus, although they are called traits, they have some properties associated with policies.

15.1.6 Member Templates versus Template Template Parameters

To implement an accumulation policy we chose to express SumPolicy and MultPolicy as ordinary classes with a member template. An alternative consists of designing the policy class interface using class templates, which are then used as template template arguments. For example, we could rewrite SumPolicy as a template:
// traits/sumpolicy2.hpp 

#ifndef SUMPOLICY_HPP 
#define SUMPOLICY_HPP 

template <typename T1, typename T2> 
class SumPolicy { 
  public: 
    static void accumulate (T1& total, T2 const & value) { 
        total += value; 
    } 
}; 

#endif // SUMPOLICY_HPP 
The interface of Accum can then be adapted to use a template template parameter:
// traits/accum8.hpp 

#ifndef ACCUM_HPP 
#define ACCUM_HPP 

#include "accumtraits4.hpp" 
#include "sumpolicy2.hpp" 

template <typename T, 
          template<typename,typename> class Policy = SumPolicy, 
          typename Traits = AccumulationTraits<T> > 
class Accum { 
  public: 
    typedef typename Traits::AccT AccT; 
    static AccT accum (T const* beg, T const* end) { 
        AccT total = Traits::zero(); 
        while (beg != end) { 
            Policy<AccT,T>::accumulate(total, *beg); 
            ++beg; 
        } 
        return total; 
    } 
}; 

#endif // ACCUM_HPP 
The same transformation can be applied to the traits parameter. (Other variations on this theme are possible: For example, instead of explicitly passing the AccT type to the policy type, it may be advantageous to pass the accumulation trait and have the policy determine the type of its result from a traits parameter.)
The major advantage of accessing policy classes through template template parameters is that it makes it easier to have a policy class carry with it some state information (that is, static data members) with a type that depends on the template parameters. (In our first approach the static data members would have to be embedded in a member class template.)
However, a downside of the template template parameter approach is that policy classes must now be written as templates, with the exact set of template parameters defined by our interface. This, unfortunately, disallows any additional template parameters in our policies. For example, we may want to add a Boolean nontype template parameter to SumPolicy that selects whether summation should happen with the += operator or whether + only should be used. In the program using a member template we can simply rewrite SumPolicy as a template:
// traits/sumpolicy3.hpp 

#ifndef SUMPOLICY_HPP 
#define SUMPOLICY_HPP 

template<bool use_compound_op = true> 
class SumPolicy { 
  public: 
    template<typename T1, typename T2> 
    static void accumulate (T1& total, T2 const & value) { 
        total += value; 
    } 
}; 

template<> 
class SumPolicy<false> { 
  public: 
    template<typename T1, typename T2> 
    static void accumulate (T1& total, T2 const & value) { 
        total = total + value; 
    } 
}; 

#endif // SUMPOLICY_HPP 
With implementation of Accum using template template parameters such an adaptation is no longer possible.

15.1.7 Combining Multiple Policies and/or Traits

As our development has shown, traits and policies don't entirely do away with having multiple template parameters. However, they do reduce their number to something manageable. An interesting question, then, is how to order such multiple parameters.
A simple strategy is to order the parameters according to the increasing likelihood of their default value to be selected. Typically, this would mean that the traits parameters follow the policy parameters because the latter are more often overridden in client code. (The observant reader may have noticed this strategy in our development.)
If we are willing to add a significant amount of complexity to our code, an alternative exists that essentially allows us to specify the nondefault arguments in any order. Refer to Section 16.1 on page 285 for details. Chapter 13 also discusses potential future template features that could simplify the resolution of this aspect of template design.

15.1.8 Accumulation with General Iterators

Before we end this introduction to traits and policies, it is instructive to look at one version of accum() that adds the capability to handle generalized iterators (rather than just pointers), as expected from an industrial-strength generic component. Interestingly, this still allows us to call accum() with pointers because the C++ standard library provides so-called iterator traits. (Traits are everywhere!) Thus, we could have defined our initial version of accum() as follows (ignoring our later refinements):
// traits/accum0.hpp 

#ifndef ACCUM_HPP 
#define ACCUM_HPP 

#include <iterator> 

template <typename Iter> 
inline 
typename std::iterator_traits<Iter>::value_type 
accum (Iter start, Iter end) 
{ 
    typedef typename std::iterator_traits<Iter>::value_type VT; 

    VT total = VT();  // assume T() actually creates a zero value 
    while (start != end) { 
        total += *start; 
        ++start; 
    } 
    return total; 
} 

#endif // ACCUM_HPP 
The iterator_traits structure encapsulates all the relevant properties of iterator. Because a partial specialization for pointers exists, these traits are conveniently used with any ordinary pointer types. Here is how a standard library implementation may implement this support:
namespace std { 
    template <typename T> 
    struct iterator_traits<T*> { 
        typedef T                          value_type; 
        typedef ptrdiff_t                  difference_type; 
        typedef random_access_iterator_tag iterator_category; 
        typedef T*                         pointer; 
        typedef T&                         reference; 
    }; 
} 
However, there is no type for the accumulation of values to which an iterator refers; hence we still need to design our own AccumulationTraits.

Type Functions

The initial traits example demonstrates that you can define behavior that depends on types. This is different from what you usually implement in programs. In C and C++, functions more exactly can be called value functions: They take some values as parameters and return another value as a result. Now, what we have with templates are type functions: a function that takes some type arguments and produces a type or constant as a result.
A very useful built-in type function is sizeof, which returns a constant describing the size (in bytes) of the given type argument. Class templates can also serve as type functions. The parameters of the type function are the template parameters, and the result is extracted as a member type or member constant. For example, the sizeof operator could be given the following interface:
// traits/sizeof.cpp 

#include <stddef.h> 
#include <iostream> 

template <typename T> 
class TypeSize { 
  public: 
    static size_t const value = sizeof(T); 
}; 

int main() 
{ 
    std::cout << "TypeSize<int>::value = " 
              << TypeSize<int>::value << std::endl; 
} 
In what follows we develop a few more general-purpose type functions that can be used as traits classes in this way.

15.2.1 Determining Element Types

For another example, assume that we have a number of container templates such as vector<T>, list<T>, and stack<T>. We want a type function that, given such a container type, produces the element type. This can be achieved using partial specialization:
// traits/elementtype.cpp 

#include <vector> 
#include <list> 
#include <stack> 
#include <iostream> 
#include <typeinfo> 

template <typename T> 
class ElementT;                    // primary template 

template <typename T> 
class ElementT<std::vector<T> > {  // partial specialization 
  public: 
    typedef T Type; 
}; 

template <typename T> 
class ElementT<std::list<T> > {    // partial specialization 
  public: 
    typedef T Type; 
}; 

template <typename T> 
class ElementT<std::stack<T> > {   // partial specialization 
  public: 
    typedef T Type; 
}; 

template <typename T> 
void print_element_type (T const & c) 
{ 
    std::cout << "Container of " 
              << typeid(typename ElementT<T>::Type).name() 
              << " elements.\n"; 
} 

int main() 
{ 
    std::stack<bool> s; 
    print_element_type(s); 
} 
The use of partial specialization allows us to implement this without requiring the container types to know about the type function. In many cases, however, the type function is designed along with the applicable types and the implementation can be simplified. For example, if the container types define a member type value_type (as the standard containers do), we can write the following:
template <typename C> 
class ElementT { 
  public: 
    typedef typename C::value_type Type; 
}; 
This can be the default implementation, and it does not exclude specializations for container types that do not have an appropriate member type value_type defined. Nonetheless, it is usually advisable to provide type definitions for template type parameters so that they can be accessed more easily in generic code. The following sketches the idea:
template <typename T1, typename T2, ... > 
class X { 
  public: 
    typedef T1  ; 
    typedef T2  ; 
     
}; 
How is a type function useful? It allows us to parameterize a template in terms of a container type, without also requiring parameters for the element type and other characteristics. For example, instead of
template <typename T, typename C> 
T sum_of_elements (C const& c); 
which requires syntax like sum_of_elements<int>(list) to specify the element type explicitly, we can declare
template<typename C> 
typename ElementT<C>::Type sum_of_elements (C const& c); 
where the element type is determined from the type function.
Note that the traits can be implemented as an extension to the existing types. Thus, you can define these type functions even for fundamental types and types of closed libraries.
In this case, the type ElementT is called a traits class because it is used to access a trait of the given container type C (in general, more than one trait can be collected in such a class). Thus, traits classes are not limited to describing characteristics of container parameters but of any kind of "main parameters."

15.2.2 Determining Class Types

With the following type function we can determine whether a type is a class type:
// traits/isclasst.hpp 

template<typename T> 
class IsClassT { 
  private: 
    typedef char One; 
    typedef struct { char a[2]; } Two; 
    template<typename C> static One test(int C::*); 
    template<typename C> static Two test(…); 
  public: 
    enum { Yes = sizeof(IsClassT<T>::test<T>(0)) == 1 }; 
    enum { No = !Yes }; 
}; 
This template uses the SFINAE (substitution-failure-is-not-an-error) principle of Section 8.3.1 on page 106. The key to exploit SFINAE is to find a type construct that is invalid for function types but not for other types, or vice versa. For class types we can rely on the observation that the pointer-to-member type construct int C::* is valid only if C is a class type.
The following program uses this type function to test whether certain types and objects are class types:
// traits/isclasst.cpp 

#include <iostream> 
#include "isclasst.hpp" 

class MyClass { 
}; 

struct MyStruct { 
}; 

union MyUnion { 
}; 

void myfunc() 
{ 
} 

enumE{e1}e; 

// check by passing type as template argument 
template <typename T> 
void check() 
{ 
    if (IsClassT<T>::Yes) { 
        std::cout << " IsClassT " << std::endl; 
    } 
    else { 
        std::cout << " !IsClassT " << std::endl; 
    } 
} 

// check by passing type as function call argument 
template <typename T> 
void checkT (T) 
{ 
    check<T>(); 
} 

int main() 
{ 
    std::cout << "int: "; 
    check<int>(); 

    std::cout << "MyClass: "; 
    check<MyClass>(); 

    std::cout << "MyStruct:"; 
    MyStruct s; 
    checkT(s); 

    std::cout << "MyUnion: "; 
    check<MyUnion>(); 

    std::cout << "enum:    "; 
    checkT(e); 

    std::cout << "myfunc():"; 
    checkT(myfunc); 
} 
The program has the following output:
int:      !IsClassT 
MyClass:  IsClassT 
MyStruct: IsClassT 
MyUnion:  IsClassT 
enum:     !IsClassT 
myfunc(): !IsClassT 

15.2.3 References and Qualifiers

Consider the following function template definition:
// traits/apply1.hpp 

template <typename T> 
void apply (T& arg, void (*func)(T)) 
{ 
    func(arg); 
} 
Consider also the following code that attempts to use it:
// traits/apply1.cpp 

#include <iostream> 
#include "apply1.hpp" 

void incr (int& a) 
{ 
    ++a; 
} 

void print (int a) 
{ 
    std::cout << a << std::endl; 
} 

int main() 
{ 
    intx=7; 
    apply (x, print); 
    apply (x, incr); 
} 
The call
apply (x, print) 
is fine. With T substituted by int, the parameter types of apply() are int& and void(*)(int), which corresponds to the types of the arguments. The call
apply (x, incr) 
is less straightforward. Matching the second parameter requires T to be substituted with int&, and this implies that the first parameter type is int& &, which ordinarily is not a legal C++ type. Indeed, the original C++ standard ruled this an invalid substitution, but because of examples like this, a later technical corrigendum (a set of small corrections of the standard; see [Standard02]) made T& with T substituted by int& equivalent to int&. [7]
[7] Note that we still cannot write int& &. This is similar to the fact that T const allows T to be substituted with int const, but an explicit int const const is not valid.
For C++ compilers that do not implement the newer reference substitution rule, we can create a type function that applies the "reference operator" if and only if the given type is not already a reference. We can also provide the opposite operation: Strip the reference operator (if and only if the type is indeed a reference). And while we are at it, we can also add or strip const qualifiers. [8] All this is achieved using partial specialization of the following generic definition:
[8] The handling of volatile and const volatile qualifiers is omitted for brevity, but they can be handled similarly.
// traits/typeop1.hpp 

template <typename T> 
class TypeOp {            // primary template 
  public: 
    typedef T         ArgT; 
    typedef T         BareT; 
    typedef T const   ConstT; 
    typedef T &       RefT; 
    typedef T &       RefBareT; 
    typedef T const & RefConstT; 
}; 
First, a partial specialization to catch const types:
// traits/typeop2.hpp 

template <typename T> 
class TypeOp <T const> {  // partial specialization for const types 
  public: 
    typedef T const   ArgT; 
    typedef T         BareT; 
    typedef T const   ConstT; 
    typedef T const & RefT; 
    typedef T &       RefBareT; 
    typedef T const & RefConstT; 
}; 
The partial specialization to catch reference types also catches reference-to-const types. Hence, it applies the TypeOp device recursively to obtain the bare type when necessary. In contrast, C++ allows us to apply the const qualifier to a template parameter that is substituted with a type that is already const. Hence, we need not worry about stripping the const qualifier when we are going to reapply it anyway:
// traits/typeop3.hpp 

template <typename T> 
class TypeOp <T&> {       // partial specialization for references 
  public: 
    typedef T &                        ArgT; 
    typedef typename TypeOp<T>::BareT  BareT; 
    typedef T const                    ConstT; 
    typedef T &                         RefT; 
    typedef typename TypeOp<T>::BareT & RefBareT; 
    typedef T const &                   RefConstT; 
}; 
References to void types are not allowed. It is sometimes useful to treat such types as plain void however. The following specialization takes care of this:
// traits/typeop4.hpp 

template<> 
class TypeOp <void> {     // full specialization for void 
  public: 
    typedef void       ArgT; 
    typedef void       BareT; 
    typedef void const ConstT; 
    typedef void       RefT; 
    typedef void       RefBareT; 
    typedef void       RefConstT; 
}; 
With this in place, we can rewrite the apply template as follows:
template <typename T> 
void apply (typename TypeOp<T>::RefT arg, void (*func)(T)) 
{ 
    func(arg); 
} 
and our example program will work as intended.
Remember that T can no longer be deduced from the first argument because it now appears in a name qualifier. So T is deduced from the second argument only, and T is used to create the type of the first parameter.

15.2.4 Promotion Traits

So far we have studied and developed type functions of a single type: Given one type, other related types or constants were defined. In general, however, we can develop type functions that depend on multiple arguments. One example that is very useful when writing operator templates are so-called promotion traits. To motivate the idea, let's write a function template that allows us to add two Array containers:
template<typename T> 
Array<T> operator+ (Array<T> const&, Array<T> const&); 
This would be nice, but because the language allows us to add a char value to an int value, we really would prefer to allow such mixed-type operations with arrays too. We are then faced with determining what the return type of the resulting template should be:
template<typename T1, typename T2> 
Array<???> operator+ (Array<T1> const&, Array<T2> const&); 
A promotion traits template allows us to fill in the question marks in the previous declaration as follows:
template<typename T1, typename T2> 
Array<typename Promotion<T1, T2>::ResultT> 
operator+ (Array<T1> const&, Array<T2> const&); 
or, alternatively, as follows:
template<typename T1, typename T2> 
typename Promotion<Array<T1>, Array<T2> >::ResultT 
operator+ (Array<T1> const&, Array<T2> const&); 
The idea is to provide a large number of specializations of the template Promotion to create a type function that matches our needs. Another application of promotion traits was motivated by the introduction of the max() template, when we want to specify that the maximum of two values of different type should have the "the more powerful type" (see Section 2.3 on page 13).
There is no really reliable generic definition for this template, so it may be best to leave the primary class template undefined:
template<typename T1, typename T2> 
class Promotion; 
Another option would be to assume that if one of the types is larger than the other, we should promote to that larger type. This can by done by a special template IfThenElse that takes a Boolean nontype template parameter to select one of two type parmeters:
// traits/ifthenelse.hpp 

#ifndef IFTHENELSE_HPP 
#define IFTHENELSE_HPP 

// primary template: yield second or third argument depending on first argument 
template<bool C, typename Ta, typename Tb> 
class IfThenElse; 

// partial specialization: true yields second argument 
template<typename Ta, typename Tb> 
class IfThenElse<true, Ta, Tb> { 
  public: 
    typedef Ta ResultT; 
}; 

// partial specialization: false yields third argument 
template<typename Ta, typename Tb> 
class IfThenElse<false, Ta, Tb> { 
  public: 
    typedef Tb ResultT; 
}; 

#endif // IFTHENELSE_HPP 
With this in place, we can create a three-way selection between T1, T2, and void, depending on the sizes of the types that need promotion:
// traits/promote1.hpp 

// primary template for type promotion 
template<typename T1, typename T2> 
class Promotion { 
  public: 
    typedef typename 
            IfThenElse<(sizeof(T1)>sizeof(T2)), 
                       T1, 
                       typename IfThenElse<(sizeof(T1)<sizeof(T2)), 
                                           T2, 
                                           void 
                                          >::ResultT 
                      >::ResultT ResultT; 
}; 
The size-based heuristic used in the primary template works sometimes, but it requires checking. If it selects the wrong type, an appropriate specialization must be written to override the selection. On the other hand, if the two types are identical, we can safely make it to be the promoted type. A partial specialization takes care of this:
// traits/promote2.hpp 

// partial specialization for two identical types 
template<typename T> 
class Promotion<T,T> { 
  public: 
    typedef T ResultT; 
}; 
Many specializations are needed to record the promotion of fundamental types. A macro can reduce the amount of source code somewhat:
// traits/promote3.hpp 

#define MK_PROMOTION(T1,T2,Tr)            \ 
    template<> class Promotion<T1, T2> {  \ 
      public:                             \ 
        typedef Tr ResultT;               \ 
    };                                    \ 
                                          \ 
    template<> class Promotion<T2, T1> {  \ 
      public:                             \ 
        typedef Tr ResultT;               \ 
    }; 
The promotions are then added as follows:
// traits/promote4.hpp 

MK_PROMOTION(bool, char, int) 
MK_PROMOTION(bool, unsigned char, int) 
MK_PROMOTION(bool, signed char, int) 
 
This approach is relatively straightforward, but requires the several dozen possible combinations to be enumerated. Various alternative techniques exist. For example, the IsFundaT and IsEnumT templates could be adapted to define the promotion type for integral and floating-point types. Promotion would then need to be specialized only for the resulting fundamental types (and user-defined types, as shown in a moment).
Once Promotion is defined for fundamental types (and enumeration types if desired), other promotion rules can often be expressed through partial specialization. For our Array example:
// traits/promotearray.hpp 

template<typename T1, typename T2> 
class Promotion<Array<T1>, Array<T2> > { 
  public: 
    typedef Array<typename Promotion<T1,T2>::ResultT> ResultT; 
}; 

template<typename T> 
class Promotion<Array<T>, Array<T> > { 
  public: 
    typedef Array<typename Promotion<T,T>::ResultT> ResultT; 
}; 
This last partial specialization deserves some special attention. At first it may seem that the earlier partial specialization for identical types (Promotion<T,T>) already takes care of this case. Unfortunately, the partial specialization Promotion<Array<T1>, Array<T2> > is neither more nor less specialized than the partial specialization Promotion<T,T> (see also Section 12.4 on page 200). [9] To avoid template selection ambiguity, the last partial specialization was added. It is more specialized than either of the previous two partial specializations.
[9] To see this, try to find a substitution of T that makes the latter become the former, or substitutions for T1 and T2 that make the former become the latter.
More specializations and partial specializations of the Promotion template can be added as more types are added for which a concept promotion makes sense.

Policy Traits

So far, our examples of traits templates have been used to determine properties of template parameters: what sort of type they represent, to which type they should promote in mixed-type operations, and so forth. Such traits are called property traits.
In contrast, some traits define how some types should be treated. We call them policy traits. This is reminiscent of the previously discussed concept of policy classes (and we already pointed out that the distinction between traits and policies is not entirely clear), but policy traits tend to be more unique properties associated with a template parameter (whereas policy classes are usually independent of other template parameters).
Although property traits can often be implemented as type functions, policy traits usually encapsulate the policy in member functions. As a first illustration, let's look at a type function that defines a policy for passing read-only parameters.

15.3.1 Read-only Parameter Types

In C and C++, function call arguments are passed "by value" by default. This means that the values of the arguments computed by the caller are copied to locations controlled by the callee. Most programmers know that this can be costly for large structures and that for such structures it is appropriate to pass the arguments "by reference-to-const" (or "by pointer-to-const" in C). For smaller structures, the picture is not always clear, and the best mechanism from a performance point of view depends on the exact architecture for which the code is being written. This is not so critical in most cases, but sometimes even the small structures must be handled with care.
With templates, of course, things get a little more delicate: We don't know a priori how large the type substituted for the template parameter will be. Furthermore, the decision doesn't depend just on size: A small structure may come with an expensive copy constructor that would still justify passing read-only parameters "by reference-to-const."
As hinted at earlier, this problem is conveniently handled using a policy traits template that is a type function: The function maps an intended argument type T onto the optimal parameter type T or T const&. As a first approximation, the primary template can use "by value" passing for types no larger than two pointers and "by reference-to-const" for everything else:
template<typename T> 
class RParam { 
  public: 
    typedef typename IfThenElse<sizeof(T)<=2*sizeof(void*), 
                                T, 
                                T const&>::ResultT Type; 
}; 
On the other hand, container types for which sizeof returns a small value may involve expensive copy constructors. So we may need many specializations and partial specializations, such as the following:
template<typename T> 
class RParam<Array<T> > { 
  public: 
    typedef Array<T> const& Type; 
}; 
Because such types are common in C++, it may be safer to mark nonclass types "by value" in the primary template and then selectively add the class types when performance considerations dictate it (the primary template uses IsClassT<> from page 266 to identify class types):
// traits/rparam.hpp 

#ifndef RPARAM_HPP 
#define RPARAM_HPP 
#include "ifthenelse.hpp" 
#include "isclasst.hpp" 

template<typename T> 
class RParam { 
  public: 
    typedef typename IfThenElse<IsClassT<T>::No, 
                                T, 
                                T const&>::ResultT Type; 
}; 

#endif // RPARAM_HPP 
Either way, the policy can now be centralized in the traits template definition, and clients can exploit it to good effect. For example, let's suppose we have two classes, with one class specifying that calling by value is better for read-only arguments:
// traits/rparamcls.hpp 

#include <iostream> 
#include "rparam.hpp" 

class MyClass1 { 
  public: 
    MyClass1 () { 
    } 
    MyClass1 (MyClass1 const&) { 
        std::cout << "MyClass1 copy constructor called\n"; 
    } 
}; 

class MyClass2 { 
  public: 
    MyClass2 () { 
    } 
    MyClass2 (MyClass2 const&) { 
        std::cout << "MyClass2 copy constructor called\n"; 
    } 
}; 
// pass MyClass2 objects with RParam<> by value 
template<> 
class RParam<MyClass2> { 
  public: 
    typedef MyClass2 Type; 
}; 
Now, you can declare functions that use RParam<> for read-only arguments and call these functions:
// traits/rparam1.cpp 

#include "rparam.hpp" 
#include "rparamcls.hpp" 

// function that allows parameter passing by value or by reference 
template <typename T1, typename T2> 
void foo (typename RParam<T1>::Type p1, 
          typename RParam<T2>::Type p2) 
{ 
     
} 

int main() 
{ 
    MyClass1 mc1; 
    MyClass2 mc2; 
    foo<MyClass1,MyClass2>(mc1,mc2); 
} 
There are unfortunately some significant downsides to using RParam. First, the function declaration is significantly more mess. Second, and perhaps more objectionable, is the fact that a function like foo() cannot be called with argument deduction because the template parameter appears only in the qualifiers of the function parameters. Call sites must therefore specify explicit template arguments.
An unwieldy workaround for this option is the use of an inline wrapper function template, but it assumes the inline function will be elided by the compiler. For example:
// traits/rparam2.cpp 

#include "rparam.hpp" 
#include "rparamcls.hpp" 

// function that allows parameter passing by value or by reference 
template <typename T1, typename T2> 
void foo_core (typename RParam<T1>::Type p1, 
               typename RParam<T2>::Type p2) 
{ 
     
} 

// wrapper to avoid explicit template parameter passing 
template <typename T1, typename T2> 
inline 
void foo (T1 const & p1, T2 const & p2) 
{ 
    foo_core<T1,T2>(p1,p2); 
} 

int main() 
{ 
    MyClass1 mc1; 
    MyClass2 mc2; 
    foo(mc1,mc2);  // same as foo_core<MyClass1,MyClass2>(mc1,mc2) 
} 

15.3.2 Copying, Swapping, and Moving

To continue the theme of performance, we can introduce a policy traits template to select the best operation to copy, swap, or move elements of a certain type.
Presumably, copying is covered by the copy constructor and the copy-assignment operator. This is definitely true for a single element, but it is not impossible that copying a large number of items of a given type can be done significantly more efficiently than by repeatedly invoking the constructor or assignment operations of that type.
Similarly, certain types can be swapped or moved much more efficiently than a generic sequence of the classic form:
T tmp(a); 
a = b; 
b = tmp; 
Container types typically fall in this category. In fact, it occasionally happens that copying is not allowed, whereas swapping or moving is fine. In the chapter on utilities, we develop a so-called smart pointer with this property (see Chapter 20).
Hence, it can be useful to centralize decisions in this area in a convenient traits template. For the generic definition, we will distinguish class types from nonclass types because we need not worry about user-defined copy constructors and copy assignments for the latter. This time we use inheritance to select between two traits implementations:
// traits/csmtraits.hpp 

template <typename T> 
class CSMtraits : public BitOrClassCSM<T, IsClassT<T>::No > { 
}; 
The implementation is thus completely delegated to specializations of BitOrClassCSM<> ("CSM" stands for "copy, swap, move"). The second template parameter indicates whether bitwise copying can be used safely to implement the various operations. The generic definition conservatively assumes that class types can not be bitwised copied safely, but if a certain class type is known to be a plain old data type (or POD), the CSMtraits class is easily specialized for better performance:
template<> 
class CSMtraits<MyPODType> 
 : public BitOrClassCSM<MyPODType, true> { 
}; 
The BitOrClassCSM template consists, by default, of two partial specializations. The primary template and the safe partial specialization that doesn't copy bitwise is as follows:
// traits/csm1.hpp 

#include <new> 
#include <cassert> 
#include <stddef.h> 
#include "rparam.hpp" 

// primary template 
template<typename T, bool Bitwise> 
class BitOrClassCSM; 

// partial specialization for safe copying of objects 
template<typename T> 
class BitOrClassCSM<T, false> { 
  public: 
    static void copy (typename RParam<T>::ResultT src, T* dst) { 
        // copy one item onto another one 
        *dst = src; 
    } 

    static void copy_n (T const* src, T* dst, size_t n) { 
        // copy n items onto n other ones 
        for (size_tk=0;k<n; ++k) { 
            dst[k] = src[k]; 
        } 
    } 

    static void copy_init (typename RParam<T>::ResultT src, 
                           void* dst) { 
        // copy an item onto uninitialized storage 
        ::new(dst) T(src); 
    } 

    static void copy_init_n (T const* src, void* dst, size_t n) { 
        // copy n items onto uninitialized storage 
        for (size_tk=0;k<n; ++k) { 
            ::new((void*)((char*)dst+k)) T(src[k]); 
        } 
    } 

    static void swap (T* a, T* b) { 
        // swap two items 
        T tmp(a); 
        *a = *b; 
        *b = tmp; 
    } 

    static void swap_n (T* a, T* b, size_t n) { 
        // swap n items 
        for (size_tk=0;k<n; ++k) { 
            T tmp(a[k]); 
            a[k] = b[k]; 
            b[k] = tmp; 
        } 
    } 

    static void move (T* src, T* dst) { 
        // move one item onto another 
        assert(src != dst); 
        *dst = *src; 
        src->~T(); 
    } 

    static void move_n (T* src, T* dst, size_t n) { 
        // move n items onto n other ones 
        assert(src != dst); 
        for (size_tk=0;k<n; ++k) { 
            dst[k] = src[k]; 
            src[k].~T(); 
        } 
    } 

    static void move_init (T* src, void* dst) { 
        // move an item onto uninitialized storage 
        assert(src != dst); 
        ::new(dst) T(*src); 
        src->~T(); 
    } 

    static void move_init_n (T const* src, void* dst, size_t n) { 
        // move n items onto uninitialized storage 
        assert(src != dst); 
        for (size_tk=0;k<n; ++k) { 
            ::new((void*)((char*)dst+k)) T(src[k]); 
            src[k].~T(); 
        } 
    } 
}; 
The term move here means that a value is transferred from one place to another, and hence the original value no longer exists (or, more precisely, the original location may have been destroyed). The copy operation, on the other hand, guarantees that both the source and destination locations have valid and identical values. This should not be confused with the distinction between memcpy() and memmove(), which is made in the standard C library: In that case, move implies that the source and destination areas may overlap, whereas for copy they do not. In our implementation of the CSM traits, we always assume that the sources and destinations do not overlap. In an industrial-strength library, a shift operation should probably be added to express the policy for shifting objects within a contiguous area of memory (the operation enabled by memmove()). We omit it for the sake of simplicity.
The member functions of our policy traits template are all static. This is almost always the case, because the member functions are meant to apply to objects of the parameter type rather than objects of the traits class type.
The other partial specialization implements the traits for bitwise types that can be copied:
// traits/csm2.hpp 

#include <cstring> 
#include <cassert> 
#include <stddef.h> 
#include "csm1.hpp" 

// partial specialization for fast bitwise copying of objects 
template <typename T> 
class BitOrClassCSM<T,true> : public BitOrClassCSM<T,false> { 
  public: 
    static void copy_n (T const* src, T* dst, size_t n) { 
        // copy n items onto n other ones 
        std::memcpy((void*)dst, (void*)src, n); 
    } 

    static void copy_init_n (T const* src, void* dst, size_t n) { 
        // copy n items onto uninitialized storage 
        std::memcpy(dst, (void*)src, n); 
    } 

    static void move_n (T* src, T* dst, size_t n) { 
        // move n items onto n other ones 
        assert(src != dst); 
        std::memcpy((void*)dst, (void*)src, n); 
    } 

    static void move_init_n (T const* src, void* dst, size_t n) { 
        // move n items onto uninitialized storage 
        assert(src != dst); 
        std::memcpy(dst, (void*)src, n); 
    } 
}; 
We used another level of inheritance to simplify the implementation of the traits for bitwise types that can be copied. This is certainly not the only possible implementation. In fact, for particular platforms it may be desirable to introduce some inline assembly (for example, to take advantage of hardware swap operations).

Afternotes

Nathan Myers was the first to formalize the idea of traits parameters. He originally presented them to the C++ standardization committee as a vehicle to define how character types should be treated in standard library components (for example, input and output streams). At that time he called them baggage templates and noted that they contained traits. However, some C++ committee members did not like the term baggage, and the name traits was promoted instead. The latter term has been widely used since then.
Client code usually does not deal with traits at all: The default traits classes satisfy the most common needs, and because they are default template arguments, they need not appear in the client source at all. This argues in favor of long descriptive names for the default traits templates. When client code does adapt the behavior of a template by providing a custom traits argument, it is good practice to typedef the resulting specializations to a name that is appropriate for the custom behavior. In this case the traits class can be given a long descriptive name without sacrificing too much source estate.
Our discussion has presented traits templates as being class templates exclusively. Strictly speaking, this does not need to be the case. If only a single policy trait needs to be provided, it could be passed as an ordinary function template. For example:
template <typename T, void (*Policy)(T const&, T const&)> 
class X; 
However, the original goal of traits was to reduce the baggage of secondary template arguments, which is not achieved if only a single trait is encapsulated in a template parameter. This justifies Myers's preference for the term baggage as a collection of traits. We revisit the problem of providing an ordering criterion in Chapter 22.
The standard library defines a class template std::char_traits, which is used as a policy traits parameter. To adapt algorithms easily to the kind of STL iterators for which they are used, a very simple std::iterator_traits property traits template is provided (and used in standard library interfaces). The template std::numeric_limits can also be useful as a property traits template, but it is not visibly used in the standard library proper. The class templates std::unary_function and std::binary_function fall in the same category and are very simple type functions: They only typedef their arguments to member names that make sense for functors (also known as function objects, see Chapter 22). Lastly, memory allocation for the standard container types is handled using a policy traits class. The template std::allocator is provided as the standard item for this purpose.
Policy classes have apparently been developed by many programmers and a few authors. Andrei Alexandrescu made the term policy classes popular, and his book Modern C++ Design covers them in more detail than our brief section (see [AlexandrescuDesign]).

Followers