在编译期想要组合策略, 除开人肉内联不算, 有很多方式. 比如弄很多很多的 bool
参数
template <bool AllowDuplicate, bool SortElements, bool CheckOutOfRange>
struct just_another_container;
这看起来就是一个参数灾难, 如果某个地方出点小的模板编译错误, 哼哼~
当然, 另一种简单的替代方案就是把所有的 bool
整合在一起, 形成一个位段
template <unsigned Policy>
struct just_another_container;
这种情况实际上缺点更多, 比如维护位域会很麻烦, 有可能在这个声明之前有这些枚举
enum {
ALLOW_DUP_MASK = 1,
SORT_ELE_MASK = 2,
CHECK_OUT_OF_RANGE_MASK = 4,
};
考虑这个容器有个 insert
函数, 它很可能需要考虑是否允许重复元素
template <unsigned AllowDuplicate>
void insert(element_type e);
而令人郁闷的是, 在 C++ 中不允许给函数偏特化, 也就是说这样写
template <>
void insert<0>(element_type e);
template <>
void insert<ALLOW_DUP_MASK>(element_type e);
是无法编译的! 要绕过这道坎, 得把 insert
函数放入一个模板结构体
template <unsigned AllowDuplicate> struct insert_s;
template <>
struct insert_s<ALLOW_DUP_MASK>
{
static void insert(just_a_container& container, element_type& e);
};
这样做代价高昂, 首先, insert
被当作孤儿一样的抛弃, 那么类中的任何非 public
成员都不再对它开放, 当然, 为了这么个简单的小玩意儿声明 friend
也是可行的, 但是多了不觉得烦吗?
另外, 在调用策略行为进行特化时一般只针对特定的位, 那么将该策略位从组合策略中分离出来, 代码中将充斥着这样的运算
Policy & ALLOW_DUP_MASK
Policy & CHECK_OUT_OF_RANGE_MASK
比如
void another_member_function()
{
element_type ele;
// wanna call insert function of policy = Policy
insert_s<*Policy & ALLOW_DUP_MASK*>::insert(*this, ele);
// ...
}
这样做非常麻烦, 如果某个地方漏了 &
或者跟错误的掩码进行了运算, 哼哼~
不过, 泛型程序设计有自己的方法, 那就是, 多继承!
不要吓到了, 是的, 在 C++ 远古时代, 多继承被认定为獠牙猛兽. 不过那都是跟 C++ 运行时多态相关的. 步入泛型新纪元的 C++ 又迎来了多继承第二春, 它可以显著改善上述问题.
将上面的声明编程
template <*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
, 使用策略不是一样麻烦吗? 假如要写出这样的代码
template <typename AllowDuplicate> struct insert_s;
template <>
struct insert_s<allow_dup>
{
static void insert(just_a_container& container, element_type& e);
};
不过慢着, 其实泛型编程不一定要跟模板扯上关系, 要记得 C++ 还有一把利器: 函数重载. 这里可以用重载来实现策略分流
void insert(element_type e, *policy_base*);
void insert(element_type e, *allow_dup*);
根据 C++ 类型匹配规则, 传入的类型将匹配最接近的类型, 虽然 allow_dup
和 policy_base
之间有继承关系, 但丝毫不必担心会有模棱两可的地方. 在调用时, 只需要简单地构造一个临时对象即可
void another_member_function()
{
element_type ele;
// wanna call insert function of policy = my_policy
insert(ele, *my_policy()*);
// ...
}
在 STL 中, 每个 iterator
内部都会定义一个类型 iterator_category
, 它会是下面几个类型之一
InputIterator
OutputIterator
ForwardIterator
BidirectionalIterator
RandomAccessIterator
其中 InputIterator
与 OutputIterator
是两个基类, 而 ForwardIterator
继承它们, 再往下 BidirectionalIterator
继承 ForwardIterator
, 处于继承树最低层的是 RandomAccessIterator
.
在调用某些 STL 算法时, 比如 std::distance
, 算法会取得该迭代器之 iterator_category
- 如果是
RandomAccessIterator
那么很简单地调用迭代器减法就行; - 对于
ForwardIterator
就只好逐个元素迭代并计数了; - 当传入的是
BidirectionalIterator
则会匹配到父类ForwardIterator
一样的方法
这种行为如果要用位值参数进行模板特化匹配来模拟, 实现会非常复杂, 而用继承结合函数重载则会让设计和编写都变得非常愉快.