2

我已经看到例如关于同一问题的相关问题,但我有一个不同的问题,我认为无法以任何其他方式解决。

这是any计算一元谓词 F对于模板参数包表示(类型列表)中的任何元素是否为真的函数P

template <template <typename...> class F, typename P>
struct any;

template <
    template <typename...> class F,
    template <typename...> class C, typename E, typename... En
>
struct any <F, C <E, En...> > :
    public _if <F <E>{}, _true, any <F, C <En...> > > { };

template <template <typename...> class F, template <typename...> class C>
struct any <F, C <> > : public _false { };

其中 my_true/_false相当于std::integral_constant <bool, true/false>, 并且_if <C, T, E>相当于typename std::conditional <C, T, E>::type(细节与问题无关,见下文)。

例如,可以写

template <typename...> struct pack { };
template <typename T> using is_int = eq <int, T>;

any <is_int, pack <int, void, float, double> >();   // evaluates to true
any <is_int, pack <char, void, float, double> >();  // evaluates to false

其中eq相当于std::is_same

二元谓词的扩展如下:

template <template <typename...> class F, typename P, typename Q>
struct any2;

template <
    template <typename...> class F,
    template <typename...> class C, typename E, typename... En,
    template <typename...> class D, typename H, typename... Hn
>
struct any2 <F, C <E, En...>, D <H, Hn...> > :
    public _if <F <E, H>{}, _true, any2 <F, C <En...>, D <Hn...> > > { };

template <
    template <typename...> class F,
    template <typename...> class C, typename Q
>
struct any2 <F, C <>, Q> : public _false { };

我们现在可以在哪里写

typedef pack <int, void, float, double> A;
typedef pack <void, float, double, int> B;
typedef pack <void, float, double, double> C;

any2 <eq, A, B>();  // false
any2 <eq, A, C>();  // true

问题来了。我们可以将方法扩展到n-ary 谓词,对n输入“包”进行操作吗?

这个问题与前一个问题不同,每个输入包的一个元素同时需要用于评估F <...>

这是一个虚构的尝试:

template <template <typename...> class F, typename... P>
struct any_n;

template <
    template <typename...> class F,
    template <typename...> class... C, typename... E, typename... En
>
struct any_n <F, C <E, En...>...> :
    public _if <F <E...>{}, _true, any_n <F, C <En...>...> > { };

template <
    template <typename...> class F,
    template <typename...> class C, typename... P
>
struct any_n <F, C <>, P...> : public _false { };

这当然不会编译。那么,一个人可以写出类似的东西C <E, En...>...吗?那会是什么类型C, E, En呢?

我怀疑答案是否定的。

这样的语法会非常方便,例如在 Scheme 宏中。我过去曾编写过此语法的 C++ 模板实现,最多支持两个级别,使用 symboldotsetcfor ...。但是获得编译器的支持会完全不同(尤其是如果您需要在同一天编译该东西)。

4

2 回答 2

5

Clang 3.3 和 GCC 4.8.1 都接受你的定义,any_n<>没有任何警告,这让我相信它是完全有效的,除了它是逻辑的事实。


为什么它不起作用?

any_n<>定义的方式中,它要求每个包的除第一个元素之外的所有元素都完全相同,因为 in struct any_n <F, C <E, En...>...>En...必须为每个展开的参数包 C<>重复。

也就是说,考虑到

template<typename...>
struct Pack;

typedef Pack<int, float, double, bool> A;
typedef Pack<void, float, double, bool> B;
typedef Pack<float, float, double, bool> C;

template<typename...>
struct SomeMetaFunction
{...};

实例化any_n<SomeMetaFunction, A, B, C>成功绑定模板参数any_n<>如下:

F => SomeMetaFunction
C => std::tuple
E... => [int, void, float]
F... => [float, double, bool]

根据其第二专业。

例如,尝试实例化any_n<SomeMetaFunction, A, B, C, D>,其中 D 定义为

typedef std::tuple <char, int, double, bool> D;

undefined any_n<>正如预期的那样导致错误。


怎么做

只需对any_n<>. 这个想法是使用递归列表的概念,就像函数式语言一样。

首先,您需要一个元函数来转换pack<a, b, c, ..., z>pack<a, pack<b, c, ... z>>.

template<typename...>
struct toRecursiveList;

//a recursive list already
template<template <typename...> class Pack, typename H, typename... T>
struct toRecursiveList<Pack<H, Pack<T...>>>
{
    using type = Pack<H, Pack<T...>>;
};

//several elements
template<template <typename...> class Pack, typename H, typename... T>
struct toRecursiveList<Pack<H, T...>>
{
    using type = Pack<H, Pack<T...>>;
};

//one element
template<template <typename...> class Pack, typename H>
struct toRecursiveList<Pack<H> >
{
    using type = Pack<H, Pack<>>;
};

//empty
template<template <typename...> class Pack>
struct toRecursiveList<Pack<>>
{
    using type = Pack<>;
};

//missing pack
template<typename H, typename... T>
struct toRecursiveList<H, T...>
{
    template<typename...>
    struct Pack;
    using type = Pack<H, Pack<T...>>;
};

的基本情况any_n<>确保参数在必要时在第一步转换为递归列表。

template <template <typename...> class F, typename... P>
struct any_n :
        public any_n<F, typename toRecursiveList<P>::type...>
{};

现在我们保证每个包最多有两个元素,因为如果没有,基本情况会将其转换为这种表示形式。使用这个事实,主递归变为以下。

template <
    template <typename...> class F,
    template <typename...> class... C, typename... E, typename... En
>
struct any_n <F, C <E, En>...> : //two elements each, no need for double pack expansion on En
    public _if <F <E...>{}, _true, any_n <F, typename toRecursiveList<En>::type...>>::type { }; 
    //                                                ^ ensures a recursive list is passed onto the next recursion step

递归保护保持不变。

template <
    template <typename...> class F,
    template <typename...> class C, typename... P
>
struct any_n <F, C <>, P...> : public _false { };

前面的示例any_n<SomeMetaFunction, A, B, C, D>现在可以按预期进行编译。

如需现场SSCCE示例,请单击此处

于 2013-09-13T02:18:29.783 回答
1

我会使用boost::mpl::zip_view一次遍历所有列表。事实上,我认为链接中的示例正是您所需要的,除了transform_view您想要的find_if

    #include <boost/mpl/zip_view.hpp>
    #include <boost/mpl/find_if.hpp>
    #include <boost/mpl/placeholders.hpp>
    #include <boost/mpl/unpack_args.hpp>
    #include <boost/mpl/vector.hpp>
    #include <boost/mpl/range_c.hpp>
    #include <boost/mpl/or.hpp>
    #include <boost/mpl/vector_c.hpp>
    #include <boost/mpl/equal_to.hpp>
    #include <boost/mpl/not_equal_to.hpp>
    #include <boost/mpl/not.hpp>
    #include <boost/mpl/and.hpp>
    #include <iostream>

    using namespace boost::mpl;
    using namespace boost::mpl::placeholders;

    template <class Predicate, class ... Sequences>
    struct any_n
    {
        typedef zip_view<vector<Sequences...> > seq;
        typedef typename not_<
            typename boost::is_same<
                typename find_if<
                    seq,
                    unpack_args<Predicate> >::type,
                typename end<seq>::type>::type>::type type;
    };

    typedef not_equal_to<boost::is_same<_1, _2>,
                         boost::is_same<_2, _3> >pred1;

    typedef or_<equal_to<_1, _2>,
                equal_to<_2, _3> > pred2;

    typedef any_n<pred1,
                  range_c<int,0,10>,
                  vector_c<unsigned, 1, 4, 2, 5>,
                  vector_c<short, 0, 0, 2, 7, 4> >::type found1;

    typedef any_n<pred2,
                  range_c<int,0,10>,
                  vector_c<unsigned, 1, 4, 2, 5>,
                  vector_c<short, 0, 0, 2, 7, 4> >::type found2;

    int main()
    {
        std::cout << std::boolalpha << found1() << ' ' << found2() << std::endl;
    }

如果您不熟悉 boost 的模板元编程方法(即Boost.TemplateMetaprogrammingLibrary),那么这似乎有点压倒性(甚至非常压倒性)。我愿意购买的唯一一本关于编程的书(至少到目前为止)是《C++ 模板元编程》,如果你对这些东西感兴趣,我强烈推荐它。
我发现这个问题指向这本书。

于 2013-09-13T02:42:43.380 回答