學習C++模板元編程(6)

來源:互聯網
上載者:User
 

接下來是第六章的一道習題,要求實現一個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演算法。 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.