Trait classes [Myers'95] solve a number of problems which arise in the
design of C++ template libraries for scientific computing.
They provide mappings from C++ *types* onto other
types, data, and code. An example of a trait class is
*numeric_limits<T>*, provided by the
ANSI/ISO C++ Standard.
The *numeric_limits<T>* trait class provides information about the
numerical properties of C++ types such as *int* and
*float*. In the ANSI C standard, these properties were provided
by constants such as FLT_MIN, FLT_MAX and FLT_EPSILON (which
represent the min, max and epsilon values for *float*). Although
these constants remain in the C++ standard, they
are not useful when writing templates, since one does not
know whether to use INT_MIN, FLT_MIN or DBL_MIN.
The *numeric_limits<T>* class solves this problem.

Consider a function template to find the largest element
in an array. One approach is to set a temporary variable equal to
a large negative value, and then loop through the array,
updating the temporary variable whenever the current element is
larger. If this function were to handle only type *float*,
then we could initialize the temporary variable to FLT_MIN (the
largest negative value which can be represented by a *float*).
However, since we want our function template to handle any
numerical type, we can use *numeric_limits<T>* to
access the appropriate minimum value for the template parameter
*T*:

template<class T>
T findMax(const T* data, int numItems)
{
// Obtain the minimum value for type T
T largest = numeric_limits<T>::min();
for (int i=0; i < numItems; ++i)
if (data[i] > largest)
largest = data[i];
return largest;
}

The *numeric_limits<T>* class provides a wealth of
information about a numeric data type: its minimum and maximum values;
how many binary and decimal digits can be considered accurate; whether
it is signed; whether it is integer-valued; the radix of its
representation (generally base 2); the machine epsilon; for floating
point types, the range of exponents in base 2 and base 10; and details
about rounding behaviour.

Having this type of information available makes it simple to write template numerical functions whose behaviour depends on the type of number representation being used.

Trait classes also simplify the implementation of arithmetic type promotion for vectors, allow a class library to track structural changes in matrices resulting from matrix operations, and can be used to simplify the interface to classes with many template parameters. The following sections summarize these uses.

```
Vector<double> double_vec; // A double-precision vector
Vector<int> int_vec; // An integer vector
Vector<complex<double> > complex_vec; // A complex vector
```

```
```

What should happen when *double_vec* is added to *complex_vec*?
Clearly the result should be a complex vector; otherwise the imaginary
component of *complex_vec* would be lost. On the other hand,
if *int_vec* is added to *double_vec*, the result should
be a double-precision vector.

Ideally, we would implement C-style arithmetic type promotions;
when vectors of two different types are added, the result should be
promoted to whatever type results in the least loss of precision.
One way of implementing this would be to provide specialized versions
of *operator+* to handle all the cases, for example:

```
```Vector<double> operator+(const Vector<double>& a, const Vector<int>& b)
{
...
}

This is clearly a tedious solution, quickly defeated by combinatorics: the specializations required to handle all combinations of type and operator number in the hundreds.

A trait class provides a better solution. Define a template class ``promote_trait'' which takes two template parameters T1 and T2. Inside the class, a publicly accessible type ``T_promote'' is declared to be the appropriate type promotion for an arithmetic operation on types T1 and T2. This is done through template specialization:

```
```template<class T1, class T2>
struct promote_trait {
typedef T1 T_promote; // Default is to promote to type T1
};
// Specialize for the <int, double> case
struct promote_trait<int, double> {
typedef double T_promote;
};
// Specialize for the <double, complex<double> > case
struct promote_trait<double, complex<double> > {
typedef complex<double> T_promote;
};
...

In practice, one would write a program to automatically generate all
the appropriate specializations of a class such as

Using this trait class, we need only provide a single version
of *operator+*. The result will be automatically
promoted to the appropriate type:

```
```template<class T1, class T2>
Vector<promote_trait<T1,T2>::T_promote>
operator+(const Vector<T1>& a, const Vector<T2>& b)
{
...
}

```
```Matrix<double, Diagonal> A; // diagonal matrix
Matrix<double, Tridiagonal> B; // tridiagonal matrix
Matrix<float, Dense> C; // dense matrix

When operations are performed on matrices, arithmetic type
promotions can be handled using the *promote_trait<T>*
class discussed previously. This will ensure that adding
a *float* matrix to a *double* matrix results
in a *double* matrix, for example.

Another promotion problem is presented by the matrix structures. Adding A+B results in a matrix with tridiagonal structure. Adding B+C results in a matrix with dense structure. Matrix multiplication also results in structure changes: multiplying A and B results in a tridiagonal matrix, but muliplying B by itself results in a banded matrix (with bandwidth 5).

These structure promotions can be handled similarly to arithmetic type promotions. Two trait classes are required-- one for matrix structure promotions under addition and subtraction, and the other for structure promotions under multiplication.

- The idea of the
*numeric_limits<T>*trait class is due to John Barton and Lee Nackman, who published a similar class in their book, ``Scientific and Engineering C++''. It was subsequently reinvented by the author and included as part of the ANSI/ISO C++ standard. - See Nathan Myer's article: Traits: a new and useful template technique (C++ Report, June 1995)
- The type tag ideas were inspired by John Vriezen. He wrote an article which I am trying to track down.

Todd Veldhuizen

Dept. of Systems Design Engineering

University of Waterloo, Waterloo, Ontario

Canada. N2L 3G1

Tel: (519) 885-1211 ext. 6087

Fax: (519) 746-3077