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 cl开发者_StackOverflow社区asses 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?
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.
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.
Seeing you can provide default values for the traits, and having the traits parameter is always more flexible, I would choose this approach unless you have some specific reasons why you cannot do it.
template<class Bar,class Traits=FooTraits<Bar> >
class Foo
{};
精彩评论