8.4 Traits and Policies

Traits
are
used throughout the C++ library. A trait is a
class or class template that characterizes a type, possibly a
template parameter. At first glance, it seems that traits obscure
information, hiding types and other declarations in a morass of
templates. This is true, but traits are also powerful tools used in
writing templates. Traits are often used to obtain information from
built-in types in the same manner as user-defined types.

A policy is a class or class template that
defines an interface as a service to other classes. Traits define
type interfaces, and policies define function interfaces, so they are
closely related. Sometimes, a single class template implements traits
and policies.

The typical application programmer might never use traits and
policies directly. Indirectly, however, they are used in the
string class, I/O streams, the standard
containers, and iteratorsjust about everywhere in the standard
library.

8.4.1 Character Traits

One of the most commonly used
trait and policy templates is
char_traits<> in the
<string> header. The standard declares two
specializations: char_traits<char> and
char_traits<wchar_t>.

The rest of the C++ library uses character traits to obtain types and
functions for working with characters. For example, the
basic_istream class template takes a character
type, charT, and a character traits type as
template parameters. The default value for the traits parameter is
char_traits<charT>, which is a set of
character traits defined in <string>. The
basic_istream template declares the get(
) function, which reads a character and returns its integer
equivalent. The return type is obtained from the character traits
template, specifically int_type.

As a policy template, char_traits<> provides
member functions that compare characters and character arrays, copy
character arrays, and so on. For example, compare
compares two character arrays for equality. The
char_traits<char> specialization might
implement compare by calling
memcmp.

At a basic level, the typical C++ programmer does not need to be
concerned with the implementation of traits. Instead, you can use the
istream and string classes, and
everything just works. If you are curious, you can trace the
declaration of, for example, istream::int_type:

Note that the declarations of basic_istream and
the other templates do not differ when the template parameter changes
from char to wchar_t. Instead,
you end up with a different template specialization for
char_traits<>, which directs you to a
different integer type.

You can implement your own character traits and policy template. For
example, suppose you want to use strings that compare themselves
without regard to case differences. Comparison is a policy issue,
typically implemented by the char_traits<>
template. You can define your own template that has the same trait
and policy implementation, but one that implements
compare to ignore case differences. Using your
template, you can specialize basic_string<>
to create a case-insensitive string class and
then store those strings in sets and maps. The keys will be compared
using your policy function that ignores case differences, as shown in
Example 8-1.

8.4.2 Iterator Traits

Traits
are also useful for iterators (Chapter 10). An
algorithm often needs to know the iterator category to provide
specializations that optimize performance for random access
iterators, for example. Traits provide a standard way to convey this
information to the algorithm namely, by using the
iterator_categorytypedef. They
also permit algorithms to use plain pointers as iterators.

For example, the distance function returns the
distance between two iterators. For random access iterators, the
distance can be computed by subtraction. For other iterators, the
distance must be computed by incrementing an iterator and counting
the number of increments needed. Example 8-2 shows a
simple implementation of distance that uses the
iterator traits to choose the optimized random access implementation
or the slower implementation for all other input iterators.

Being able to optimize
algorithms
for certain kinds of iterators is one benefit of using traits, but
the real power comes from the
iterator_traits<T*> specialization. This
class permits the use of any pointer type as an iterator. (See
<iterator> in Chapter 13
for details.) Consider how the distance function
is called in the following example:

The compiler infers the InputIter template
parameter as type int*. The
iterator_traits<T*> template is expanded to
obtain the iterator_category type
(random_access_iterator_tag) and
difference_type (ptrdiff_t).

8.4.3 Custom Traits

Traits can be useful whenever you are
using templates. You never know what the template parameters might
be. Sometimes, you want to specialize your own code according to a
template parameter.

For example, all the standard sequence containers have a constructor
that takes two iterators as arguments:

But take a closer look at the declaration. The
author's intent is clear: that the template
parameter must be an input iterator, but nothing in the declaration
enforces this restriction. The compiler allows any type to be used
(at least any type that can be copied).

If the
InputIterator type actually is an input iterator, the
list is constructed by copying all the elements in the range
[first, last). But if the
InputIterator type is an integral type, the
first argument is interpreted as a count, and the
last argument is interpreted as an integer value,
which is converted to the value type of the container; the container
is then initialized with first copies of the
last value, which is ordinarily the work of a
different constructor. See Chapter 10 for more
information about these constructors.

If you need to
implement your own container
template, you must find a way to implement this kind of constructor.
The simplest way is to define a traits template that can tell you
whether a type is an integral type. Example 8-3
shows one possible implementation and how it can be used by a
container.