# Type traits

... by popular demand. Here we go! We take a look at how to obtain a type-traits implementation for C++, what they are good for and go through some easy examples to understand what they do.

### Where to get them

Before we start, you'll need a type-traits implementation.
Boost has a very nice one, which is fully
modularized. You can include it by using `#include`

which will put it
into the `std::tr1`

namespace that I will use in the examples.
Optionally, the TR1 additions to C++ contain it (for example, the Visual
C++ Feature Pack has TR1 support).

### What are they

Well, type-traits allow you to make compile-time decisions based on the
properties of a type. Sounds kinda complicated? Well, think about it
like this: You have a template class, and the user passes in a type you
never heard about. With type-traits, you can get information about the
type. The result of a type-trait is passed by its member `value`

which
is either `true`

or `false`

. Moreover, there is a `type`

which is a real
type (`integral_constant`

, but I'll omit it here and write a small
`bool`

-wrapper-type). We'll be using this helper structure which turns a
boolean into a type:

struct True {}; struct False {}; template <bool b> struct BoolToType; template <> struct BoolToType<true> { typedef True type; }; template <> struct BoolToType<false> { typedef False type; };

Obviously, `BoolToType<true>::type`

is `True`

. This comes in handy
later.

### How to make use of them

Let's assume we are implementing a copy routine:
`fast_copy (const T* source, T* destination, size_type count)`

. Now, if
`T`

has a trivial assign implementation (trivial meaning an assign
implementation which is just a `memcpy`

) we *can* just use `memcpy`

,
otherwise we have to go into a loop and copy using `T::operator=`

. Now,
how can we make the decision at compile-time whether it is a type with a
trivial assign or not? You guessed it, with type-traits, specifially
`has_trivial_assign`

.

#include <type_traits> #include <cstring> #include <string> #include "boolToType.h" // BoolToType helper // Fast path template <typename T, typename size_type> void fast_copy_impl (const T* source, T* destination, const size_type count, const True&) { std::memcpy (destination, source, count * sizeof (T)); } // Slow path template <typename T, typename size_type> void fast_copy_impl (const T* source, T* destination, const size_type count, const False&) { for (size_type i = size_type (); i < count; ++i) { *destination++ = *source++; } } template <typename T, typename size_type> void fast_copy (const T* source, T* destination, const size_type count) { BoolToType<std::tr1::has_trivial_assign<T>::value>::type canUseFastPath; fast_copy_impl (source, destination, count, canUseFastPath); } int main () { std::string arrComplex [10]; int arrSimple [20]; // Will call the loop version fast_copy (arrComplex, arrComplex + 5, 5); // VC++9 can roll this out to direct copy via registers (!) fast_copy (arrSimple, arrSimple + 10, 10); }

We turn the boolean result of the type-traits into a type by using the
helper (as said above, you could directly overload based on
`integral_constant`

), and use it to dispatch between two template
functions. Voilà , based on the type, we can choose to use `memcpy`

if
possible. And the good thing is, this *will work for any type*. This is
something you have no chance to implement on your own as this type-trait
relies on (non-portable) compiler support! Another example, how to skip
calling `T::~T`

if the destructor does nothing. This can help if you
have a large array, and you want to get rid of it quickly - if the
destructor is trivial, you can just throw it away, otherwise, you need
to clear it. This can be interesting if you have some "scratch-memory"
where you allocate blocks from. Here we go:

template <typename T, typename size_type> void array_delete_impl (T* array, const size_type count, const False&) { for (size_type i = size_type (); i < count; ++i) { array[i].~T(); } } template <typename T, typename size_type> void array_delete_impl (T* array, const size_type count, const True&) {} template <typename T, typename size_type> void array_delete (T* array, const size_type count) { array_delete_impl (array, count, BoolToType<std::tr1::has_trivial_destructor<T>::value>::type ()); }

### Conclusion

Type-traits are a very powerful tool which can both improve the
efficiency of template code and improve the maintainability by not
relying on custom traits specialisations. The examples here are just
very basic - just take a look at how many type traits are available. In
addition, there are special type-traits like `add_reference`

which
transform types: They add a qualifier if possible (otherwise, you could
wind up having a `const T& &`

which is not allowed in current C++ --
will be allowed in C++0x).