About
RSS

Bit Focus


泛型编程中的策略类

Posted at 2009-11-22 08:56:30 | Updated at 2018-05-12 05:21:43

在编译期想要组合策略, 除开人肉内联不算, 有很多方式. 比如弄很多很多的 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_duppolicy_base 之间有继承关系, 但丝毫不必担心会有模棱两可的地方. 在调用时, 只需要简单地构造一个临时对象即可

void another_member_function()
{
    element_type ele;
    // wanna call insert function of policy = my_policy
    insert(ele, *my_policy()*);
    // ...
}

在 STL 中, 每个 iterator 内部都会定义一个类型 iterator_category, 它会是下面几个类型之一

其中 InputIteratorOutputIterator 是两个基类, 而 ForwardIterator 继承它们, 再往下 BidirectionalIterator 继承 ForwardIterator, 处于继承树最低层的是 RandomAccessIterator.

在调用某些 STL 算法时, 比如 std::distance, 算法会取得该迭代器之 iterator_category

这种行为如果要用位值参数进行模板特化匹配来模拟, 实现会非常复杂, 而用继承结合函数重载则会让设计和编写都变得非常愉快.

Post tags:   Generic Programming  C++  Template

Leave a comment:




Creative Commons License Your comment will be licensed under
CC-NC-ND 3.0


. Back to Bit Focus
NijiPress - Copyright (C) Neuron Teckid @ Bit Focus
About this site