Traits:


emplate <class ForwardIterator1, class ForwardIterator2>
void iter_swap( ForwardIterator1 it1, ForwardIterator2 it2)
{
    T tmp = *it1;  // what is the type of T ???
    *it1  = *it2;
    *it2  = tmp;
}

template <class ForwardIterator1, class ForwardIterator2>
void iter_swap( ForwardIterator1 it1, ForwardIterator2 it2)
{
    typename ForwardIterator1::value_type tmp = *it1;
    *it1  = *it2;
    *it2  = tmp;
}

But:

void f(int *p1, int *p2)
{
    iter_swap(p1,p2);   // error! 
}


Pointers have no value_type member.
This should be added in non-intrusive way:


template <class Iterator> struct iterator_traits;

template <class ForwardIterator1, class ForwardIterator2>
void iter_swap( ForwardIterator1 it1, ForwardIterator2 it2)
{
    typename terator_traits<ForwardIterator1>::value_type tmp = *it1;
    *it1  = *it2;
    *it2  = tmp;
}

// iterator traits for pointer types:
template <>
struct iterator_traits<T*>
{
    typedef T  value_type;
    typedef T& reference;
    typedef T* pointer;
    typedef ptrdiff_t difference_type;
    typedef random_access_iterator_tag iterator_category;
};

// default implementation:
template <class Iterator>
struct iterator_traits
{
    typedef typename Iterator::value_type value_type;
    //...
};



Character traits:


template<class Ch, class Tr = char_traits<Ch>, class A = allocator<Ch> >
class std::basic_string
{
public:
    // ...
};


template<class Ch> struct char_traits { };  // no default implementation

template<> struct char_traits<char>
{   // char_traits operations should not throw exceptions
    typedef char char_type;     // type of character

    static void assign(char_type&, const char_type&);   // = for char_type

    // integer representation of characters:
    typedef int int_type;       // type of integer value of character

    static char_type to_char_type(const int_type&); // int to char conversion
    static int_type to_int_type(const char_type&);  // char to int conversion
    static bool eq_int_type(const int_type&, const int_type&);  // ==

    // char_type comparisons:
    static bool eq(const char_type&, const char_type&); // ==
    static bool lt(const char_type&, const char_type&); // <

    // operations on s[n] arrays:
    static char_type* move(char_type* s, const char_type* s2, size_t n);
    static char_type* copy(char_type* s, const char_type* s2, size_t n);
    static char_type* assign(char_type* s, size_t n, char_type a);

    static int compare(const char_type* s, const char_type* s2, size_t n);
    static size_t length(const char_type*);
    static const char_type* find(const char_type* s, int n, const char_type&);

    // I/O related:
    typedef streamoff off_type;     // offset in stream
    typedef streampos pos_type;     // position in stream
    typedef mbstate_t state_type;   // multi-byte stream state

    static int_type eof();              // end-of-file
    static int_type not_eof(const int_type& i); // i unless i equals eof(); if not any value!=eof()
    static state_type get_state(pos_type p);    // multibyte conversion state of character in p
};

template<> struct char_traits<wchar_t>
{
    typedef wchar_t char_type;
    typedef wint_t int_type;
    typedef wstreamoff off_type;
    typedef wstreampos pos_type;

    // like char_traits<char>
};



Usage:


#ifndef CISTRING_H
#define CISTRING_H

#include <string>

//using namespace std;

struct ci_char_traits : public std::char_traits<char>
{
    static bool eq( char c1, char c2)
    {
        return toupper(c1) == toupper(c2);
    }
    static bool lt( char c1, char c2)
    {
        return toupper(c1) < toupper(c2);
    }
    static int compare( const char *s1, const char *s2, size_t n)
    {
        return memicmp( s1, s2, n);     // non-standard !
    }
    static const char *find( const char *s, int n, char ch)
    {
        while ( n-- > 0  &&  toupper(*s) != toupper(ch) )
        {
            ++s;
        }
        return n > 0 ? s : 0;
    }
};

typedef std::basic_string<char, ci_char_traits> ci_string;

#endif /* CISTRING_H */



Let see traits in a complex environment.
This is an ordinary matrix implementation:


template <class T>
class matrix
{
public:
    // ...
    ~matrix();
    matrix operator=( const matrix &other);
private:
    int  x;
    int  y;
    T   *v;
    void copy( const matrix &other);
    // ...
};

// ...

template <class T>
matrix<T> matrix<T>::operator=( const matrix &other)
{
    if ( this != &other )
    {
        delete [] v;
        copy( other);
    }
    return *this;
}
template <class T>
void matrix<T>::copy( const matrix &other)
{
    x = other.x;
    y = other.y;
    v = new T[x*y];
    for ( int i = 0; i < x*y; ++i )
        v[i] = other.v[i];
}



For the most cases we use POD types as template parameters.
Therefore we can optimize the copy function for POD types:


template <class T>
void matrix<T>::copy( const matrix &other)
{
    std::cerr << "elementary copy in loop" << std::endl;

    x = other.x;
    y = other.y;
    v = new T[x*y];
    for ( int i = 0; i < x*y; ++i )
        v[i] = other.v[i];
}
template <>
void matrix<long>::copy( const matrix &other)
{
    std::cerr << "bitwise copy for long" << std::endl;

    x = other.x;
    y = other.y;
    v = new long[x*y];
    memcpy( v, other.v, sizeof(long)*x*y);
}



Unfortunately, the solution causes scattered code.
We can make a more structured solution using traits.


template <typename T>
struct copy_trait
{
    static void copy( T* to, const T* from, int n)
    {
        // default copy with T::operator= 
        for( int i = 0; i < n; ++i )
            to[i] = from[i];
    }
};
template <>
struct copy_trait<long>
{
    static void copy( long* to, const long* from, int n)
    {
        // bitwise copy
        memcpy( to, from, n*sizeof(long));
    }
};
template <>
struct copy_trait<double>
{
    static void copy( double* to, const double* from, int n)
    {
        // bitwise copy
        memcpy( to, from, n*sizeof(double));
    }
};

template <class T, class Cpy = copy_trait<T> >
class matrix
{
public:
   // ...
};

template <class T, class Cpy>
void matrix<T,Cpy>::copy( const matrix &other)
{
    x = other.x;
    y = other.y;
    v = new T[x*y];

    // calling the appropriate copy 
    Cpy::copy( v, other.v, x*y);
}


It is better, because we outfactored the copy implementation from matrix.
But we have to repeat the copy function for all POD types. Lets improve it!


template <typename T>
struct is_pod
{
    enum { value = false };
};

template <>
struct is_pod<long>
{
    enum { value = true };
};

template <typename T, bool B>
struct copy_trait
{
    static void copy( T* to, const T* from, int n)
    {
        // default copy
        for( int i = 0; i < n; ++i )
            to[i] = from[i];
    }
};
template <typename T>
struct copy_trait<T, true>
{
    static void copy( T* to, const T* from, int n)
    {
        // bitwise copy
        memcpy( to, from, n*sizeof(T));
    }
};

template <class T, class Cpy = copy_trait<T,is_pod<T>::value> >
class matrix
{
    // ..
};



Using template metaprogramming, we can make an even better solution:


typedef TYPELIST_4(char, signed char, unsigned char, int)  Pod_types;

template <typename T>
struct is_pod
{
    enum { value = ::Loki::TL::IndexOf<Pod_types,T>::value != -1 };
};

template <typename T, bool B>
struct copy_trait
{
    static void copy( T* to, const T* from, int n)
    {
        for( int i = 0; i < n; ++i )
            to[i] = from[i];
    }
};
template <typename T>
struct copy_trait<T, true>
{
    static void copy( T* to, const T* from, int n)
    {
        memcpy( to, from, n*sizeof(T));
    }
};

template <class T, class Cpy = copy_trait<T,is_pod<T>::value> >
class matrix
{
    // ...
};