学习C++模板元编程(6)

 
接下来是第六章的一道习题,要求实现一个 binary_tree_inserter,以使用mpl::copy算法从其它序列生成一棵二分查找树(Binary Search Tree,即满足以下条件的一种二分树:左子树所有元素小于根,右子树所有元素大于根,且左右子树全都是二分查找树)。习题中给出的测试代码如下:
typedef mpl::copy<
      mpl::vector_c<int,17,25,10,2,11>
    , binary_tree_inserter< tree<> >
    >::type bst;
 
//       int_<17>
//       /      /
//    int_<10> int_<25>
//     /    /
// int_<2> int_<11>
BOOST_STATIC_ASSERT(( mpl::equal<
      inorder_view<bst>
    , mpl::vector_c<int,2,10,11,17,25>
    >::value ));
 
使用 mpl::copy算法依据一个mpl::vector<>生成二分查找树,生成过程中使用一个inserter依次将mpl::vector<>中的各个元素插入到开始为空的tree<>中,inserter要确保插入后,tree<>保持满足二分查找树的条件。
明显, inserter要做的就是:从当前tree<>的根开始,如果要插入的元素小于根,则选择左子树插入,否则选择右子树插入;左右子树的插入方法与上同,重复以上步骤。
参照书中给出的关于 inserter的例子, binary_tree_inserter 应该是这样的:
template <class S, class T>
struct push_bst;
 
template <class S>
struct binary_tree_inserter
    : mpl::inserter<
        S,
        push_bst<_, _>
    >
{
};
 
其中有一个 push_bst是辅助类,它执行实际的插入算法。 binary_tree_inserter 基于push_bst实现。留意binary_tree_inserter定义中的push_bst<_, _>,使用了mpl的占位符(placeholder),占位符的确是一个非常有意思,也非常有用的工具。它可以很方便地将mpl::copy算法在调用binary_tree_inserter时传入的参数,转送到push_bst,不需要你显式地定义出来,大大简化了我们编写MPL程序的工作。
接着看看 push_bst的实现。push_pst的实现分为三种情况:插入到空的树中、插入到只有一个元素的树中、插入非平凡的树中。为了表示空的树,还要定义一个none类来表示空的元素。根据这些想法,修改tree的定义如下:
struct none {};
 
template <class R = none, class LC = none, class RC = none>
struct tree
{
    typedef tree type;
    typedef R root;
    typedef LC left;
    typedef RC right;
};
 
空的树既可以直接用 none表示,也可以表示为tree<none, none, none>,所以push_bst的第一种情况可以写为:
template <class T>
struct push_bst<none, T>
    : T
{
};
 
template <class T>
struct push_bst<tree<>, T>
    : tree<T>
{
};
 
push_bst的第三种情况(插入到一棵真正的 tree中)写为:
template <class R, class LC, class RC, class T>
struct push_bst<tree<R, LC, RC>, T>
    : mpl::eval_if<
        typename mpl::less<T, R>::type,
        tree<R, typename push_bst<LC, T>::type, RC>,
        tree<R, LC, typename push_bst<RC, T>::type>
    >
{
};
 
先判断插入的元素 T是否小于被插入树的根,是则将T插入到左子树后形成新的树,否则将T插入右子树后形成新的树。
最后是 push_bst的第二种情况(插入到只有一个元素的树中,且该树使用该元素直接表示),这也是push_bst的主模板,代码如下:
template <class S, class T>
struct push_bst
    : mpl::eval_if<
        typename mpl::less<T, S>::type,
        tree< S, T >,
        tree< S, none, T >
    >
{
};
 
代码与前一种情况相似,只不过这次不是用 tree<>来表示树,而是直接以元素本身表示。
其实,第二、三种情况也可以合并起来写,不过就需要另外实现几个辅助类,分别用于取出二分树的根、左子树和右子树。如果给出的是普通的树,取出这几样东西都很容易(因为我们已经在 tree<>的定义里typedef了这几样东西);但是如果给出的是退化的表示方法(即以元素本身来表示只含单个元素的树)的话,就必须使用模板偏特化来实现了:
template <class T>
struct root
{
    typedef T type;
};
 
template <class R, class LC, class RC>
struct root< tree<R, LC, RC> >
{
    typedef R type;
};
 
template <class T>
struct left_child
{
   typedef none type;
};
 
template <class R, class LC, class RC>
struct left_child< tree<R, LC, RC> >
{
    typedef LC type;
};
 
template <class T>
struct right_child
{
    typedef none type;
};
 
template <class R, class LC, class RC>
struct right_child< tree<R, LC, RC> >
{
    typedef RC type;
};
 
有了以上辅助类,就可以将 push_bst的后两种情况合并为一种写法:
template <class S, class T>
struct push_bst
    : mpl::eval_if<
        typename mpl::less<T, typename root<S>::type>::type,
        tree< 
            typename root<S>::type, 
            typename push_bst<typename left_child<S>::type, T>::type, 
            typename right_child<S>::type 
        >,
        tree< 
            typename root<S>::type, 
            typename left_child<S>::type,
            typename push_bst<typename right_child<S>::type, T>::type
        >
    >
{
};
 
当然,另两个 push_bst的特化版本(push_bst<none, T>和push_bst<tree<>, T>)还得保留。不过,比较这两种写法,后一种写法虽然减少了push_bst的特化版本,但是引入了多个辅助类,如果这些辅助类没有其它用途的话,我觉得还是前一种写法更简练些。
在进入到最后的测试代码之前,还要稍微修改一下上一篇给出的 inorder_view,这是因为这次我们加入了一个none,而上一篇给出的inorder_view没有考虑none的情况。改动其实很少,增加一个针对none的特化版本就行了:
template <>
struct inorder_view<none> : mpl::vector<>
{
};
 
照例,最后是测试用的代码:
int main()
{
    typedef mpl::copy<
          mpl::vector_c<int,17,25,10,2,11>
        , binary_tree_inserter< tree<> >
        >::type bst;
 
    BOOST_STATIC_ASSERT(( mpl::equal<
          inorder_view<bst>::type
        , mpl::vector_c<int,2,10,11,17,25>
        >::value ));
    
    return 0;
}
 
这次的 inserter实现比上一次的xxxorder_view相比稍微复杂一些,但也不太难,关键是要理解清楚mpl中的inserter的原理,剩下就都好办了。下一篇将会实现一个完整的编译期Binary Tree,包括了一整套的begin、end、iterator、deref、next、prior等等,然后是在此基础上实现的binary_tree_search算法。
 
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值