# 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).