这两周学习了《
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; }
|
应该说,这个开头还是比较容易的,不过后面就有点难度了。