2

たとえば、同じ問題に関する関連する質問を見たことがありますが、別の問題があり、他の方法では解決できないと思います。

テンプレート引数パック表現 (型のリスト) のいずれかの要素に対して単項述語anyが true であるかどうかを計算する関数を次に示します。 FP

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 predicateへのアプローチを拡張できますか?n

この問題は、各入力パックの 1 つの要素が の評価に同時に必要であるという点で、前の問題とは異なり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 マクロなどで非常に便利です。dots過去に、シンボルまたはetcforを使用して、最大 2 つのレベルをサポートするこの構文の C++ テンプレート実装を作成しました...。しかし、コンパイラからのサポートがある場合はまったく異なります (特に、同じ日にコンパイルする必要がある場合)。

4

2 に答える 2

5

Clang 3.3 と GCC 4.8.1 は両方ともany_n<>、警告なしであなたの定義を受け入れます。これは、それが論理であるという事実に加えて、それが完全に有効であると私に信じさせるものです。


機能しないのはなぜですか?

が定義されている方法any_n<>では、すべてのパックの最初の要素を除くすべてがまったく同じでstruct any_n <F, C <E, En...>...>あるEn...必要があります。 in は、展開されるすべてのパラメーター パック 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]

その2番目の専門分野によると。

たとえば、インスタンス化しようとすると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...>
{};

現在、すべてのパックが最大 2 つの要素を持つことが保証されています。この事実を利用すると、主な再帰は次のようになります。

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.TemplateMetaprogrammingLibrary )に慣れていない場合、これはやや圧倒される (または非常に圧倒されることさえある) ように思えるかもしれません。私がプログラミングに関して(少なくともこれまでのところ)喜んで購入した唯一の本は「C++ Template Metaprogramming」であり、この内容に興味がある場合は強くお勧めします。とりわけこの本を指すこの
質問 を見つけました。

于 2013-09-13T02:42:43.380 に答える