bool
stemplate <bool AllowDuplicate, bool SortElements, bool CheckOutOfRange>
struct just_another_container;
bool
, if you make a mistake on the order of arguments, you could hardly discover it before the program goes mad.Another approach is, to merge all
bool
s to a single unsigned
, liketemplate <unsigned Policy>
struct just_another_container;
enum {
ALLOW_DUP_MASK = 1,
SORT_ELE_MASK = 2,
CHECK_OUT_OF_RANGE_MASK = 4,
};
insert
interface to the container, which is concerned about whether or not allows duplicated elements in the container, the code may look likevoid insert(element_type e)
{
_insert<Policy & ALLOW_DUP_MASK>(e);
}
template <>
void _insert<0>(element_type e);
template <>
void _insert<ALLOW_DUP_MASK>(element_type e);
_insert
into a template struct
, liketemplate <unsigned AllowDuplicate>
struct insert_s
{
static void insert(just_a_container& container, element_type& e);
};
template <>
struct insert_s<ALLOW_DUP_MASK>
{
static void insert(just_a_container& container, element_type& e);
};
struct insert_s
, It should be granted public access to just_a_container
.Besides, in the code there would be full of bitwise-and here and there like
void another_member_function()
{
element_type ele;
/* wanna call insert function of policy = Policy */
insert_s<Policy & ALLOW_DUP_MASK>::insert(*this, ele);
/* ... */
find_s<Policy & SORT_ELE_MASK>::insert(*this, ele);
/* ... */
}
&
is omitted or wrong flag is used.But luckily, generice programming gets its own way --- multiple inheritance.
If you hates Object-Oriented programming, don't feel depressive please. This time it's not about polymorphism. It's all traits.
Ok, let's get down to the code. Change
just_another_container
intotemplate <typename Policy>
struct just_another_container;
struct policy_base {};
struct allow_dup : public policy_base {};
struct sort_ele : public policy_base {};
struct check_out_of_range : public policy_base {};
struct my_policy : public allow_dup, public sort_ele {};
just_another_container<my_policy> my_container;
insert
function signature. This time, function overloading will be our friend.void insert(element_type e, policy_base);
void insert(element_type e, allow_dup);
insert
by passing an instance of my_policy
, the overload void insert(element_type e, allow_dup)
will be called. For examplevoid another_member_function()
{
element_type ele;
/* wanna call insert function of policy = my_policy */
insert(ele, my_policy());
}
In STL, each
iterator
(among std::vector::iterat
, std::list::iterator
, etc) will have an iterator_category
defined, which would be one of the followingInputIterator
OutputIterator
ForwardIterator
BiirectionalIterator
RandomAccessIterator
InputIterator
and OutputIterator
are the 2 base types, ForwardIterator
inherits both of them, then follows BidirectionalIterator
, which inherits ForwardIterator
, and the RandomAccessIterator
, a sub-class of ForwardIterator
.Some STL algorithm will be concerned about
iterator_category
, and use it to match the most efficient overload. For example, std::distance
will- simply use minus, if category is
RandomAccessIterator
; - count element one by one, if category is exactly
ForwardIterator
; - still count element one by one, if
BidirectionalIterator
, the sub-type ofForwardIterator
is passed.