如何交换mpl :: vector的两个元素?

时间:2011-10-28 14:56:08

标签: c++ boost boost-mpl

我正在编写一个模板函数,它应该交换boost::mpl::vector的两个元素(类似于std::swap)。困难的部分是在编译期间没有变量的概念。我写了一个草稿,但我想知道是否有更好的方法来解决这个问题。

我当前的代码草图从迭代器中提取了一个整数索引,并执行了交换元素的序列类型的副本。问题是 - 这可以做得更好:

#include <boost/mpl/distance.hpp>
#include <boost/mpl/begin_end.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/eval_if.hpp>
#include <boost/mpl/comparison.hpp>
#include <boost/mpl/equal.hpp>
#include <boost/mpl/clear.hpp>
#include <boost/mpl/next_prior.hpp>
#include <boost/mpl/push_back.hpp>
#include <boost/mpl/at.hpp>
#include <boost/mpl/or.hpp>

using boost::mpl::distance;
using boost::mpl::begin;
using boost::mpl::end;
using boost::mpl::next;
using boost::mpl::at;
using boost::mpl::or_;
using boost::mpl::int_;
using boost::mpl::eval_if;
using boost::mpl::greater;
using boost::mpl::equal;
using boost::mpl::clear;
using boost::mpl::push_back;


namespace boost { namespace mpl {

template<template<typename, typename> class T, class A, class B>
struct eval2 {
    typedef typename T<typename A::type, typename B::type>::type type;
};


namespace details {

    template <typename Dest_seq, typename It_end, typename It_first, typename It_second, typename It_idx>
    struct copy_and_swap {
    private:
        typedef typename eval_if< is_same<It_idx, It_first>,
                                  eval2<push_back, Dest_seq, deref<It_second> >,
                                  eval_if<is_same<It_idx, It_second>,
                                          eval2<push_back, Dest_seq, deref<It_first> >,
                                          eval2<push_back, Dest_seq, deref<It_idx> >
                                         >
                                >::type Limit_idx;
        typedef typename next<It_idx>::type it_idx_next;

    public:
        // next step
        typedef typename eval_if <is_same<it_idx_next, It_end>,
                                  New_seq,
                                  copy_and_swap<New_seq, 
                                                It_end, 
                                                It_first, 
                                                It_second, 
                                                it_idx_next>
                                 >::type type;
    };

} // namespace details


template<typename Seq, typename Begin, typename End>
struct swap {
  private:
    typedef typename begin<Seq>::type                it_begin;
    typedef typename end<Seq>::type                  it_end;
    // get an empty container type "compatible" with Seq
    typedef typename clear<Seq>::type        Container_t;
    // border case - swap self
    typedef typename is_same<Begin, End>::type   swap_self;
    // border case - less than 2 elements in sequence
    typedef typename less<size<Seq>, int_<2> >::type    no_swap;

  public:
    // perform the element swapping
    typedef typename eval_if <or_<swap_self, no_swap>,
                              Seq,
                              details::copy_and_swap<Container_t,
                                                     it_end,
                                                     Begin,
                                                     End,
                                                     it_begin >
                             >::type type;
};

} // namespace mpl
} // namespace boost

这个元函数可以像:

一样使用
struct value_printer {
    template< typename U > void operator()(U x) {
        std::cout << x << ',';
    }
};



typedef vector_c<int, 1, 2, 3, 6, 5, 4>::type    test_vect;
typedef begin<test_vect>::type    it_beg;
typedef advance<it_beg, int_<2> >::type    it;
typedef advance<it_beg, int_<5> >::type    it_stop;
typedef m_swap<test_vect, it_stop, it>::type    result;
boost::mpl::for_each< result >( value_printer() );

,结果是1,2,4,6,5,3,

1 个答案:

答案 0 :(得分:1)

这是一个仅使用MPL元函数的解决方案,没有显式递归。我们的想法是首先将序列开头和第一个值之间的所有值复制到交换,插入第二个值,复制中间,插入第一个值,最后复制结束。

此方法的缺点是迭代器必须形成有效范围:Second不得早于First。我不认为有任何办法可以通过这个解决方案来克服这个限制,但这似乎不是一个无法忍受的要求。

以下是代码:

// Precondition: [First, Second] is a valid range in Seq
template< typename Seq, typename First, typename Second >
struct swap {
  private:
    typedef typename begin< Seq >::type begin;
    typedef typename end< Seq >::type   end;

    typedef typename clear< Seq >::type empty_container;

    // Insert values from begin to first
    typedef typename
        copy<
            iterator_range< begin, First >,
            back_inserter< empty_container >
        >::type prefix;

    // Insert second value 
    typedef typename
        push_back<
            prefix, typename
            deref< Second >::type
        >:: type prefixSecond;

    // Insert values from first+1 to second
    typedef typename
        copy<
            iterator_range< typename next< First >::type, Second >,
            back_inserter< prefixSecond >
        >::type prefixSecondMiddle;

    // Insert first value
    typedef typename
        push_back<
            prefixSecondMiddle, typename
            deref< First >::type
        >::type prefixSecondMiddleFirst;

    // Insert values from second+1 to end
    typedef typename
        copy<
            iterator_range< typename next< Second >::type, end >,
            back_inserter< prefixSecondMiddleFirst >
        >::type prefixSecondMiddleFirstSuffix;

  public:
    typedef prefixSecondMiddleFirstSuffix type;
};