這兩周學習了《C++ TMP》第五、六章,是關於TMP的序列(容器)、迭代器和演算法。做了不少習題,其中最有意思的(也是花了我最多時間的)習題是用TMP實現二分尋找樹(Binary Search Tree)。從第五章的一道關於二分樹的習題開始。題目給出一個編譯期的二分樹資料結構,例如:
typedef tree< // double
double // / /
, tree<void*,int,long> // void* char
, char // / /
> tree_seq; // int long
要求實現對該二分樹的前序、中序、後序遍曆,即滿足:
BOOST_STATIC_ASSERT(( mpl::equal<
preorder_view<tree_seq>
, mpl::vector<double,void*,int,long,char>
, boost::is_same<_1,_2>
>::value ));
BOOST_STATIC_ASSERT(( mpl::equal<
inorder_view<tree_seq>
, mpl::vector<int,void*,long,double,char>
, boost::is_same<_1,_2>
>::value ));
BOOST_STATIC_ASSERT(( mpl::equal<
postorder_view<tree_seq>
, mpl::vector<int,long,void*,char,double>
, boost::is_same<_1,_2>
>::value ));
其實這三種遍曆是很相似的,實現了其中一個,另兩個就可以照著寫了。所以我們先從preorder_view開始著手。當然,最開始還是要先定義tree,如下:
template <class R, class LC, class RC> struct tree { typedef tree type; typedef R root; typedef LC left; typedef RC right; }; |
其中type的定義是為了滿足boost::mpl的慣例,其餘三個定義顯而易見,是為了可以方便地取出tree類的根、左子樹和右子樹。接著,我們來看看如何?preorder_view。從題目給出的測試代碼可知,preorder_view的調用結果應該是類似於mpl::vector<>的一個序列,裡面是按前序的方式對tree進行遍曆所得到的元素。所以,我乾脆就用mpl::vector<>來作為preorder_view的調用結果,先寫出主模板的初稿:
template <class T> struct preorder_view : mpl::vector<T> {}; |
再針對tree<>類進行偏特化,如下:
template <class R, class LC, class RC> struct preorder_view< tree<R, LC, RC> > { typedef mpl::vector<R>s t1; typedef typename mpl::insert_range<t1, typename mpl::end<t1>::type, typename preorder_view<LC>::type >::type t2; typedef typename mpl::insert_range<t2, typename mpl::end<t2>::type, typename preorder_view<RC>::type >::type type; }; |
這段代碼是按照前序的方式來實現,即先訪問根項目,然後以前序方式訪問左子樹,再以前序方式訪問右子樹。這個偏特化版本與主模板一道,就可以實現前序走訪了。相似地,我們可以寫出inorder_view和postorder_view,如下:
template <class T> struct inorder_view : mpl::vector<T> {}; template <class R, class LC, class RC> struct inorder_view< tree<R, LC, RC> > { typedef typename inorder_view<LC>::type t1; typedef typename mpl::insert_range<t1, typename mpl::end<t1>::type, mpl::vector<R> >::type t2; typedef typename mpl::insert_range<t2, typename mpl::end<t2>::type, typename inorder_view<RC>::type >::type type; }; template <class T> struct postorder_view : mpl::vector<T> {}; template <class R, class LC, class RC> struct postorder_view< tree<R, LC, RC> > { typedef typename postorder_view<LC>::type t1; typedef typename mpl::insert_range<t1, typename mpl::end<t1>::type, typename postorder_view<RC>::type >::type t2; typedef typename mpl::insert_range<t2, typename mpl::end<t2>::type, mpl::vector<R> >::type type; }; |
最後是測試代碼:
int main() { typedef tree< double, tree<void*,int,long>, char > tree_seq; BOOST_STATIC_ASSERT(( mpl::equal< preorder_view<tree_seq>::type , mpl::vector<double,void*,int,long,char> >::value )); BOOST_STATIC_ASSERT(( mpl::equal< inorder_view<tree_seq>::type , mpl::vector<int,void*,long,double,char> >::value )); BOOST_STATIC_ASSERT(( mpl::equal< postorder_view<tree_seq>::type , mpl::vector<int,long,void*,char,double> >::value )); return 0; } |
應該說,這個開頭還是比較容易的,不過後面就有點難度了。