Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Traits and passing traits as template parameters

When is it practical to pass traits as template parameters rather than simply using some existing traits struct like

typedef basic_ofstream< char, char_traits<char> >

vs.

typedef basic_ofstream< char >?

I have some tile classes which I would like to have some common ground (traits), so I designed tile_traits to contain all the basic info about a tile, such as int_type and flag_type, like so:

//unspecialized
template<typename T> struct tile_traits;
//... other stuff here, declaration of a tile class
template<>
struct tile_traits<tile_class>
{
   typedef tile_class::int_type  int_type;
   typedef tile_class::flag_type flag_type;
   //other possible tile info here.
}

Is designing traits as such considered a traits-blob?

like image 595
Khaled Nassar Avatar asked Feb 11 '11 07:02

Khaled Nassar


2 Answers

The design of traits is as much art as anything else. There are no hard and fast answers here. I believe this question has gone unanswered because it is impossible to give a good answer without knowing a lot more about the problem you are solving.

In general traits classes are a useful "customization point". That is, if you are designing a template:

template <class Tile>
class TileContainer
{
    ...
};

TileContainer might make use of tile_traits<Tile> for some properties of Tile. And the client of TileContainer can specialize tile_traits<MyTile> in order to communicate variations of the properties when the default trait (if it exists) is not correct.

So far I don't think I've said anything you don't already know (judging from the way your question is worded).

I think your question is:

Should you design:

A)

template <class Tile, class Traits = tile_traits<Tile>>
class TileContainer
{
    // uses Traits
};

or:

B)

template <class Tile>
class TileContainer
{
    // uses tile_traits<Tile>
};

There are examples of both designs in the C++03 and upcoming C++0x standards.

Example A designs:

template<class charT, class traits = char_traits<charT>,
                      class Allocator = allocator<charT>>
    class basic_string;  // both traits and Allocator are traits

template <class Codecvt, class Elem = wchar_t,
                         class Tr = char_traits<Elem>>
    class wbuffer_convert;

template <class T, class Allocator = allocator<T>>
    class vector; // Allocator is a A-trait that uses another
                  // B-trait internally:  allocator_traits<Allocator>

template <class charT, class traits = regex_traits<charT>>
    class basic_regex;

Example B designs:

template<class Iterator> struct iterator_traits;
template <class Alloc> struct allocator_traits;
template <class Ptr> struct pointer_traits;
template <class Rep> struct treat_as_floating_point;
template <class Rep> struct duration_values;

My only advice is that there is no right or wrong design. Use:

template <class Tile>
class TileContainer
{
    // uses tile_traits<Tile>
};

when you are sure that your customer's needs can always be met by specializing tile_traits<MyTile>.

Use:

template <class Tile, class Traits = tile_traits<Tile>>
class TileContainer
{
    // uses Traits
};

when you suspect that your customer may need different traits for the same Tile, or when you want to force the type of TileContainer to be different when some trait other than tile_traits is used.

like image 83
Howard Hinnant Avatar answered Sep 22 '22 14:09

Howard Hinnant


You need to have the traits class as a template parameter if you can see that people would pass different traits for the same data type. If your tiles will always have the same tile_traits for each T, you can use that directly.

If you can see that someone, sometimes, will use a my_special_traits, you need to have that as a separate template parameter.

like image 30
Bo Persson Avatar answered Sep 21 '22 14:09

Bo Persson