【问题标题】:Making a variadic templete from another variadic template从另一个可变参数模板制作可变参数模板
【发布时间】:2020-03-10 10:06:42
【问题描述】:

说实话,我不知道如何开始寻找我试图解决的问题的解决方案。可能已经有解决方案了。所以任务来了。

我有一个实际上是带有 2 个参数的模板的类:

template <typename F, typename S>
class trans {...};

我还有另一个类,它包含这些“trans”类的链,如元组(示例):

class holder {
    using chain_type = std::tuple<trans<std::string, int>, 
                                  trans<int, float>, 
                                  trans<float, std::string>, 
                                  trans<std::string, json>>;
};

并且可以看出,“trans”的每个第二个参数都与下一个第一个参数相同。链条:

std::string -> int -> float -> std::string -> json.

我想要什么...我想要一些方法来制作这样的链:

template <typename ...Args>
class holder {
    using chain_type = trans_chain_create_t<Args...>;
};

holder<std::string, int, float, std::string, json> h;

有可能吗? 我对可变参数模板不是很熟悉,很少使用它们。

【问题讨论】:

  • 附录,将static_assert( sizeof...( Args ) &gt;= 2, "There must be at least two types to create a chain." ); 添加到holder 类会更好地警告用户关于其可变参数模板参数的要求。

标签: c++ c++17 variadic-templates


【解决方案1】:

是的,有可能:

template< typename F, typename S >
class trans {};

template< typename F, typename S, typename... Tail >
struct create_trans_chain;

template< typename F, typename S, typename... Tail >
using create_trans_chain_t = typename create_trans_chain< F, S, Tail... >::type;

template< typename F, typename S >
struct create_trans_chain< F, S >
{
    using type = std::tuple< trans< F, S > >;
};

template< typename F, typename S, typename Next, typename... Tail >
struct create_trans_chain< F, S, Next, Tail... >
{
    using type = decltype(std::tuple_cat(
        std::declval< create_trans_chain_t< F, S > >(),
        std::declval< create_trans_chain_t< S, Next, Tail... > >()));
};

【讨论】:

  • 哇。没想到这么简单:)非常感谢!
【解决方案2】:

对于Boost.Mp11,这很短(一如既往):

template <typename ...Args>
using trans_chain_create_t =
    mp_transform<trans,
        mp_pop_back<std::tuple<Args...>>,
        mp_pop_front<std::tuple<Args...>>>;

mp_transform 基本上是一个zip,我们将(Args 不带尾部)与(Args 不带头部)同时应用trans


您可以通过添加辅助元函数 zip_tail 来拆分上述内容:

template <template <typename...> class F, typename L>
using zip_tail = mp_transform<F, mp_pop_back<L>, mp_pop_front<L>>;

template <typename ...Args>
using trans_chain_create_t = zip_tail<trans, std::tuple<Args...>>;

【讨论】:

    【解决方案3】:

    只需展开具有结束特化的递归模板。它的工作原理在 cmets 的代码中进行了描述。看看:

    class json; // as you like that in your given code example... we need to define it
    using input = std::tuple< std::string, int, float, std::string, json >;
    
    // First we define a template struct which takes 1 parameter
    // No need for a definition as we specialize later
    template <typename INPUT >
    struct Transform;
    
    // for all inputs which have at minimum 3 template parameters 
    // inside the std::tuple parameter we use this specialization 
    template <typename FIRST, typename SECOND, typename NEXT, typename ... TAIL >
    struct Transform< std::tuple<FIRST, SECOND, NEXT, TAIL...>>
    {
        // As we have more than 2 parameters, we continue to transform
        // simply by using a recursive "call" to out Transform
        // struct
        using OUT = decltype( std::tuple_cat( 
            std::tuple< std::pair< FIRST, SECOND >>(),
            std::declval<typename Transform< std::tuple<SECOND, NEXT, TAIL...>>::OUT>()
            ));        
    };
    
    // This specialization is used for the last input as
    // it has exactly 2 parameters  
    template <typename FIRST, typename SECOND >
    struct Transform< std::tuple<FIRST, SECOND >>
    {
        using OUT = typename std::tuple<std::pair < FIRST, SECOND>>;
    };
    
    using OUT = Transform< input >::OUT;
    
    template < typename T>
    void Print()
    {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
    
    int main()
    {
        Print< Transform< input >::OUT >();
    }
    

    没有必要定义你自己的template <typename F, typename S> class trans {...};,因为我们有std::pair

    【讨论】:

    【解决方案4】:

    来不及玩了?

    如果您想要一个非递归解决方案...std::tuple_element 是您的朋友。 (编辑:嗯......显然是非递归的:正如 Andrey Semashev 所指出的,std::tuple_element 本身很可能是递归的)。

    给定一个声明的(观察:未定义;它仅用于decltype())辅助函数,如下所示

    template <typename T, std::size_t ... Is>
    constexpr auto getChain (std::index_sequence<Is...>)
       -> std::tuple<trans<std::tuple_element_t<Is, T>,
                           std::tuple_element_t<Is+1u, T>>...>;
    

    你的trans_chain_create_t 简单地(没有递归)变成

    template <typename ... Args>
    struct trans_chain_create
     { using type = decltype(getChain<std::tuple<Args...>>
                       (std::make_index_sequence<sizeof...(Args)-1u>{})); };
    
    template <typename ... Args>
    using trans_chain_create_t = typename trans_chain_create<Args...>::type;
    

    下面是一个完整编译(C++14就够了)的例子

    #include <tuple>
    #include <string>
    #include <utility>
    
    template <typename, typename>
    class trans
     { };
    
    class json
     { };
    
    template <typename T, std::size_t ... Is>
    constexpr auto getChain (std::index_sequence<Is...>)
       -> std::tuple<trans<std::tuple_element_t<Is, T>,
                           std::tuple_element_t<Is+1u, T>>...>;
    
    template <typename ... Args>
    struct trans_chain_create
     { using type = decltype(getChain<std::tuple<Args...>>
                       (std::make_index_sequence<sizeof...(Args)-1u>{})); };
    
    template <typename ... Args>
    using trans_chain_create_t = typename trans_chain_create<Args...>::type;
    
    template <typename ... Args>
    struct holder
     { using chain_type = trans_chain_create_t<Args...>; };
    
    holder<std::string, int, float, std::string, json> h;
    int main ()
     {
       using H = holder<std::string, int, float, std::string, json>;
       using CT1 = typename H::chain_type;
       using CT2 = std::tuple<trans<std::string, int>, 
                              trans<int, float>, 
                              trans<float, std::string>, 
                              trans<std::string, json>>;
    
       static_assert( std::is_same_v<CT1, CT2>, "!" );
     }
    

    【讨论】:

    • 事情是,tuple_element 本身很可能是递归的。
    • @AndreySemashev - 嗯......我怀疑你是对的......你已经说服了我:添加了另一个答案,将你的解决方案转换为非递归的。希望std::tuple_cat() 不是递归的。
    【解决方案5】:

    从 Andrey Semashev 的回答中汲取灵感……非递归(也没有 std::tuple_element)版本。

    给定一些声明的函数(不需要定义:仅在decltype()内部使用)

    template <std::size_t N, std::size_t I, typename, typename>
    constexpr std::enable_if_t<(I == N), std::tuple<>> filter ();
    
    template <std::size_t N, std::size_t I, typename T1, typename T2>
    constexpr std::enable_if_t<(I < N), std::tuple<trans<T1, T2>>> filter ();
    
    template <std::size_t N, typename ... Ts1, typename ... Ts2,
              std::size_t ... Is>
    constexpr auto getChain (std::tuple<Ts1...>, std::tuple<Ts2...>,
                             std::index_sequence<Is...>)
       -> decltype(std::tuple_cat(filter<N, Is, Ts1, Ts2>()...));
    

    你可以这样写trans_chain_create(_t)

    template <typename T, typename ... Ts>
    struct trans_chain_create
     {
       using Tpl1 = std::tuple<T, Ts...>;
       using Tpl2 = std::tuple<Ts..., T>;
       using IndS = std::make_index_sequence<sizeof...(Ts)+1u>;
    
       using type = decltype(getChain<sizeof...(Ts)>
                       (std::declval<Tpl1>(), std::declval<Tpl2>(), IndS{}));
     };
    
    template <typename ... Args>
    using trans_chain_create_t = typename trans_chain_create<Args...>::type;
    

    下面是一个完整编译(C++14就够了)的例子

    #include <tuple>
    #include <string>
    #include <utility>
    
    template <typename, typename>
    class trans
     { };
    
    class json
     { };
    
    template <std::size_t N, std::size_t I, typename, typename>
    constexpr std::enable_if_t<(I == N), std::tuple<>> filter ();
    
    template <std::size_t N, std::size_t I, typename T1, typename T2>
    constexpr std::enable_if_t<(I < N), std::tuple<trans<T1, T2>>> filter ();
    
    template <std::size_t N, typename ... Ts1, typename ... Ts2,
              std::size_t ... Is>
    constexpr auto getChain (std::tuple<Ts1...>, std::tuple<Ts2...>,
                             std::index_sequence<Is...>)
       -> decltype(std::tuple_cat(filter<N, Is, Ts1, Ts2>()...));
    
    template <typename T, typename ... Ts>
    struct trans_chain_create
     {
       using Tpl1 = std::tuple<T, Ts...>;
       using Tpl2 = std::tuple<Ts..., T>;
       using IndS = std::make_index_sequence<sizeof...(Ts)+1u>;
    
       using type = decltype(getChain<sizeof...(Ts)>
                       (std::declval<Tpl1>(), std::declval<Tpl2>(), IndS{}));
     };
    
    template <typename ... Args>
    using trans_chain_create_t = typename trans_chain_create<Args...>::type;
    
    template <typename ... Args>
    struct holder
     { using chain_type = trans_chain_create_t<Args...>; };
    
    holder<std::string, int, float, std::string, json> h;
    
    int main ()
     {
       using H = holder<std::string, int, float, std::string, json>;
       using CT1 = typename H::chain_type;
       using CT2 = std::tuple<trans<std::string, int>, 
                              trans<int, float>, 
                              trans<float, std::string>, 
                              trans<std::string, json>>;
    
       static_assert( std::is_same_v<CT1, CT2>, "!" );
     }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-12-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-14
      • 2012-03-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多