簡體   English   中英

可變參數模板模板,再次

[英]Variadic variadic template templates, again

我已經看到例如在同一問題上的一個相關問題 ,但是我有一個不同的問題,我認為無法以任何其他方式解決。

這是any函數,它計算模板參數包表示形式(類型列表) P任何元素的一元謂詞 F是否為true:

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 { };

其中我的_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進制謂詞 ,在操作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 ++模板實現,最多支持兩個級別,將符號dotsetc用於... 但是獲得編譯器的支持是完全不同的(特別是如果您需要在同一天進行編譯)。

Clang 3.3和GCC 4.8.1都接受了您對any_n<>定義,而沒有發出警告,這使我相信它是完全有效的,除了它是邏輯。


為什么不起作用?

any_n<>的定義方式,它要求每個包中除第一個元素外的所有元素都完全相同,因為在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<>的定義進行少量修改,就可以完全實現所需的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示例,請單擊此處

我將使用boost::mpl::zip_view一次遍歷所有列表。 實際上,我認為鏈接中的示例正是您需要的,除了您需要find_if而不是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 ++模板元編程”,如果您對此內容感興趣,我強烈推薦它。
我發現這個問題尤其指向本書。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM