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

发布于 2024-12-12 07:30:34 字数 4134 浏览 1 评论 0原文

我正在编写一个模板函数,它应该交换 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,

I'm writing a template function which should swap two elements of a boost::mpl::vector (similarly to std::swap). The difficult part is there is no concept of a variable during compile time. I have written a draft but I wonder if there are better ways to approach this.

My current code sketch extracts an integral index from iterators and performs a copy of the sequence type with the elements swapped. The question is - can this be done better:

#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

This metafunction can be used like:

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() );

and the result is 1,2,4,6,5,3,

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

◇流星雨 2024-12-19 07:30:34

这是一个仅使用 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;
};

Here is a solution using only MPL metafunctions, without explicit recursion. The idea is to start by copying all the values between the beginning of the sequence and the first value to swap, insert the second value, copy the middle, insert the first value, and finally copy the end.

A disadvantage of this method is that the iterators must form a valid range: Second must not be before First. I don't think there is any way to overcome this restriction with this solution, but it does not seem like an unbearable requirement.

Here is the code:

// 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;
};
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文