使用boost::mpl组合类型

4

我有一个类型列表,我想用其中的元素构建所有两个元素组合的列表。例如:

namespace mpl = boost::mpl;
typedef mpl::vector<int, long> typelist;
// mpl magic...
// the wanted list is equivalent to:
typedef mpl::vector<pair<int, int>, pair<int, long>,
                    pair<long, int>, pair<long, long> > combinations;

这里,pair<T1,T2> 可以是 std::pair<T1,T2> 或者 mpl::vector<T1,T2>。如何实现呢?当我们考虑到 pair<T1, T2> == pair<T2, T1> 时,我也希望能够去除重复的内容。
谢谢。
3个回答

6

通过调用 mpl::fold,可以计算单个类型 int 与类型列表 mpl::vector<int, long> 的所有组合:

typedef fold<
    mpl::vector<int, long>, vector<>, 
    push_back<mpl::_1, std::pair<int, mpl::_2> > 
>::type list_of_pairs;

现在,如果我们将其封装为一个单独的元函数,并对初始类型列表的所有类型调用它,我们会得到:
typedef mpl::vector<int, long> typelist;

template <typename T, typename Result>
struct list_of_pairs
  : mpl::fold<typelist, Result, 
        mpl::push_back<mpl::_1, std::pair<T, mpl::_2> > > 
{};

typedef mpl::fold<
    typelist, mpl::vector<>, mpl::lambda<list_of_pairs<mpl::_2, mpl::_1> >
>::type result_type;

BOOST_MPL_ASSERT(
    mpl::equal<result_type, 
        mpl::vector4<
            std::pair<int, int>, std::pair<int,long>,
            std::pair<long,int>, std::pair<long,long> 
        > >::value);

编辑:回答第二个问题:

使结果仅包含唯一元素(按照您所述的意义)需要更多的工作。首先,您需要定义一个元函数,比较两个元素并返回mpl :: true_ / mpl :: false_:

template <typename P1, typename P2>
struct pairs_are_equal
  : mpl::or_<
        mpl::and_<
            is_same<typename P1::first_type, typename P2::first_type>,
            is_same<typename P1::second_type, typename P2::second_type> >,
        mpl::and_<
            is_same<typename P1::first_type, typename P2::second_type>, 
            is_same<typename P1::second_type, typename P2::first_type> > >
{};

接下来我们需要定义一个元函数,它会尝试在给定的列表中查找给定的元素:

template <typename List, typename T>
struct list_doesnt_have_element
  : is_same<
        typename mpl::find_if<List, pairs_are_equal<mpl::_1, T> >::type, 
        typename mpl::end<List>::type>
{};

现在,这可以用于创建一个新列表,确保不插入重复项:
typedef mpl::fold<
    result_type, mpl::vector<>,
    mpl::if_<
        mpl::lambda<list_doesnt_have_element<mpl::_1, mpl::_2> >, 
        mpl::push_back<mpl::_1, mpl::_2>, mpl::_1>

>::type unique_result_type;

这些都是我脑海中的想法,可能需要进行一些微调。但是这个思路应该是正确的。


编辑:根据@rafak的概述进行了一些小修正。


非常感谢!我不知道如何使用mpl::lambda。有了你在回答中提供的有关mpl的材料,我现在想我能够回答自己的问题了。 - rafak
请注意:我无法编辑您的帖子,因此在list_doesnt_have_element中,请将end<List>替换为typename end<List>::type,对于find_if也是如此。另外,我不得不将BOOST_STATIC_ASSERT(is_same<替换为BOOST_MPL_ASSERT((mpl::equal<,并在末尾添加另一个括号。如果有人可以编辑答案,我将删除此评论。 - rafak

2

很好的问题,解决方法有很多有趣的方式。以下是其中一种。

所有不合格的名称都在mpl命名空间中,除了_1_2,它们位于mpl::placeholdersboost::is_same中,后者可以在type_traits库中找到。第一个模板是一个帮助类,用于生成由单个元素和给定序列中的每个元素组成的所有对的列表。第二个模板将所有结果聚合在一起形成最终序列。请注意,结果不在一个向量中。您可以使用mpl::copy轻松完成此操作。

template <class Elem, class Seq>
struct single_combo {
    typedef typename transform<Seq
            ,lambda< std::pair<Elem, _1> >
        >::type type;
};

template <class Seq>
struct combo {
    typedef typename unique<Seq, is_same<_1,_2> >::type U;
    typedef typename fold<
        typename transform<U
            ,lambda< single_combo<_1, U> >
            >::type
        ,empty_sequence
        ,lambda< joint_view<_1,_2> >
    >::type type;
};

typedef typename combo<typelist>::type combinations;

附注:如果您正在阅读本文并想挑战自己,请尝试回答这个问题。这是深入了解MPL的好机会。


非常好的答案!谢谢。我不知道views。所以我试图用transform_view替换fold内部的transform,看起来它的效果符合预期。但是如果我更改single_combo中的转换(尽管直接使用可以工作,但从combo中不行),这种方法就不起作用了。你有解释吗? - rafak

0
我最近也在进行一些元编程方面的工作,你有研究过 boost::mpl::set 吗?那可以消除重复项。至于组合,则听起来像是映射,那 boost::mpl::map 呢?请注意,库会对序列的类型限制施加限制,尽管这可以通过宏进行调整,但你仍然要受到编译器上限的限制,具体取决于需要处理的类型数量。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接