在编译期想要组合策略, 除开人肉内联不算, 有很多方式. 比如弄很多很多的 bool 参数
template
struct just_another_container;这看起来就是一个参数灾难, 如果某个地方出点小的模板编译错误, 哼哼~
当然, 另一种简单的替代方案就是把所有的 bool 整合在一起, 形成一个位段
template
struct just_another_container;这种情况实际上缺点更多, 比如维护位域会很麻烦, 有可能在这个声明之前有这些枚举
enum {
ALLOW_DUP_MASK = 1,
SORT_ELE_MASK = 2,
CHECK_OUT_OF_RANGE_MASK = 4,
};考虑这个容器有个 insert 函数, 它很可能需要考虑是否允许重复元素
template
void insert(element_type e);而令人郁闷的是, 在 C++ 中不允许给函数偏特化, 也就是说这样写
template <>
void insert<0>(element_type e);
template <>
void insert(element_type e);是无法编译的! 要绕过这道坎, 得把 insert 函数放入一个模板结构体
template struct insert_s;
template <>
struct insert_s
{
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 struct insert_s;
template <>
struct insert_s
{
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, 它会是下面几个类型之一
InputIteratorOutputIteratorForwardIteratorBidirectionalIteratorRandomAccessIterator
其中 InputIterator 与 OutputIterator 是两个基类, 而 ForwardIterator 继承它们, 再往下 BidirectionalIterator 继承 ForwardIterator, 处于继承树最低层的是 RandomAccessIterator.
在调用某些 STL 算法时, 比如 std::distance, 算法会取得该迭代器之 iterator_category
- 如果是
RandomAccessIterator那么很简单地调用迭代器减法就行; - 对于
ForwardIterator就只好逐个元素迭代并计数了; - 当传入的是
BidirectionalIterator则会匹配到父类ForwardIterator一样的方法
这种行为如果要用位值参数进行模板特化匹配来模拟, 实现会非常复杂, 而用继承结合函数重载则会让设计和编写都变得非常愉快.