【问题标题】:Expanding with pack of templates使用模板包进行扩展
【发布时间】:2016-05-13 23:51:08
【问题描述】:

定义template_pack,如下例所示。给定

template <typename> struct A;
template <typename, typename, typename> struct B;
template <typename, typename> struct C;

然后

template_pack<std::tuple<char, bool, double>, A, B, C>::type

将成为

std::tuple<A<char>, B<char, bool, double>, C<char, bool>>

即总是在元组中从左到右读取,以便获得足够的类型来适应每个模板。

到目前为止,这是我的解决方案。但它只适用于最多 3 种类型的模板,我看不出如何推广到任何类型的模板:

#include <type_traits>
#include <tuple>

template <template <typename...> class Template, typename... Ts> struct use_template;

template <template <typename> class Template, typename A, typename... Rest>
struct use_template<Template, A, Rest...> {
    using type = Template<A>;
};

template <template <typename, typename> class Template, typename A, typename B, typename... Rest>
struct use_template<Template, A, B, Rest...> {
    using type = Template<A,B>;
};

template <template <typename, typename, typename> class Template, typename A, typename B, typename C, typename... Rest>
struct use_template<Template, A, B, C, Rest...> {
    using type = Template<A,B,C>;
};

template <typename Pack, template <typename...> class... Templates> struct template_pack;

template <template <typename...> class P, typename... Ts, template <typename...> class... Templates>
struct template_pack<P<Ts...>, Templates...> {
    using type = P<typename use_template<Templates, Ts...>::type...>;
};

// Testing
template <typename> struct A;
template <typename, typename, typename> struct B;
template <typename, typename> struct C;

int main() {
    static_assert (std::is_same<
        template_pack<std::tuple<char, bool, double>, A, B, C>::type,
        std::tuple<A<char>, B<char, bool, double>, C<char, bool>>
    >::value, "");
}

如何概括以上内容?有没有可能?

【问题讨论】:

    标签: c++ templates tuples c++14 variadic-templates


    【解决方案1】:

    这是“查找最短前缀”。

    实用程序:

    // pack that is easy to append to
    template<class... Ts> 
    struct pack{
        template<class... T1s>
        using append = pack<Ts..., T1s...>;
    };
    
    template<class...> using void_t = void;
    

    肉:

    // find the shortest proper prefix Ts... of [T, Rest...]
    // such that A<Ts...> is well-formed, and return it.
    template<template<class...> class A, // template we are going to look at
             class AlwaysVoid,           // for void_t
             class Current,              // pack containing the types we are testing next
             class T,                    // next type to add to pack if test fails
             class... Rest>              // remaining types
    struct find_viable
        : find_viable<A, AlwaysVoid, typename Current::template append<T>, Rest...> {};
    
    // picked if A<Ts...> is well-formed
    template<template<class...> class A, class... Ts, class T, class...Rest>
    struct find_viable<A, void_t<A<Ts...>>, pack<Ts...>, T, Rest...> {
        using type = A<Ts...>;
    };
    
    // Finds the shortest prefix of Ts... such that A<prefix...> is well formed.
    // the extra void at the end is slightly hackish - find_viable only checks
    // proper prefixes, so we add an extra dummy type at the end.
    template<template<class...> class A, class... Ts>
    using find_viable_t = typename find_viable<A, void, pack<>, Ts..., void>::type;
    

    然后使用它:

    template <typename Pack, template <typename...> class... Templates> struct template_pack;
    
    template <template <typename...> class P, typename... Ts,
              template <typename...> class... Templates>
    struct template_pack<P<Ts...>, Templates...> {
        using type = P<find_viable_t<Templates, Ts...>...>;
    };
    

    【讨论】:

      【解决方案2】:

      我不会全部写出来,但我会做一个演练。

      template<template<class...>class Z>
      struct z_template{
        template<class...Ts>using result=Z<Ts...>;
      };
      

      这让您可以像类型一样操作模板。

      template<template<class...>class Z, class...Ts>
      using can_apply = /* ... */;
      

      这会问“Z&lt;Ts...&gt; 有效吗?如果有效,则返回 true_type”。

      template<class...>struct types{using type=types;};
      

      这是一个轻量级的模板包。

      template<template<class...>class Z, class types>
      using apply = /* ... */;
      

      这需要一个模板Z 和一个types&lt;Ts...&gt;,并返回Z&lt;Ts...&gt;

      template<class Z, class types>
      using z_apply = apply<typename Z::template result, types>;
      

      这是通过 z_template 参数而不是 template 参数来实现的。

      尽可能频繁地处理类型会使多种元编程变得更容易。

      接下来我们写

      template<class types>using pop_front = // ...
      template<class types>using reverse = // ...
      template<class types>using pop_back = reverse<pop_front<reverse<types>>>;
      

      types的前后移除元素。

      template<class Result>
      struct z_constant {
        template<class...Ts>using result=Result;
      };
      

      这会找到可以有效传递给Z::template result&lt;???&gt;types 的最长前缀。已经完成了一些避免传递太短序列的工作,以防它们“迟到”失败:

      template<class Z, class types>
      struct z_find_longest_prefix :
        std::conditional_t<
          can_apply< z_apply, Z, types >{},
          z_constant<types>,
          z_template<longest_prefix>
        >::template result<Z, pop_back<types>>
      {};
      template<class Z, class types>
      using z_find_longest_prefix_t = typename z_find_longest_prefix<Z,types>::type;
      
      struct empty_t {};
      template<class Z>
      struct z_find_longest_prefix<Z,types<>>:
        std::conditional_t<
          can_apply< Z::template result >,
          types<>,
          empty_t
        >
      {};
      

      如果没有有效的前缀,find_longest_prefix 将无法编译。

      template<class Z, class types>
      using z_apply_longest_prefix_t =
        z_apply< Z, z_find_longest_prefix_t<Z, types> >;
      
      template<class src, template<class...>class... Zs>
      struct use_template;
      template<class src, template<class...>class... Zs>
      using use_template_t = typename use_template<src, Zs...>::type;
      
      template<template<class...>class Z0, class...Ts, template<class...>class... Zs>
      struct use_template< Z0<Ts...>, Zs... > {
        using type=Z0<
          z_apply_longest_prefix_t<
            z_template<Zs>, types<Ts...>
          >...
        >;
      };
      

      直到拼写错误和类似问题,完成。

      可以清理很多。可能不需要整个z_ 子系统。每当进行严肃的元编程时,我都会倾向于使用它,因为突然之间,我编写的任何适用于类型的元函数现在都可以在模板上使用。

      reverse 需要做一些工作才能高效地完成。

      can_applymany of my posts 中可用。

      pop_front 是微不足道的。

      apply 应该很简单。

      【讨论】:

        【解决方案3】:

        这看起来很有趣,所以我尝试了一下;我并不是说这比现有答案更好,但也许有些人会发现它更清楚:

        namespace detail {
        
        template<typename...>
        using void_t = void;
        
        template<template<typename...> class, typename, typename, typename EnableT = void>
        struct fill_template;
        
        template<template<typename...> class TemplateT, typename TupleT, std::size_t... Is>
        struct fill_template<
            TemplateT, TupleT, std::index_sequence<Is...>,
            void_t<TemplateT<std::tuple_element_t<Is, TupleT>...>>
        >
        {
            using type = TemplateT<std::tuple_element_t<Is, TupleT>...>;
        };
        
        template<
            template<typename...> class TemplateT, typename TupleT,
            std::size_t I, std::size_t N
        >
        struct fill_template_dispatcher
        {
            template<typename T, typename = typename T::type>
            static constexpr std::true_type test(int) { return {}; }
            template<typename T>
            static constexpr std::false_type test(long) { return {}; }
        
            using candidate_t = fill_template<TemplateT, TupleT, std::make_index_sequence<I>>;
        
            using type = typename std::conditional_t<
                test<candidate_t>(0),
                candidate_t,
                fill_template_dispatcher<TemplateT, TupleT, I + 1, I != N ? N : 0>
            >::type;
        };
        
        template<template<typename...> class TemplateT, typename TupleT, std::size_t I>
        struct fill_template_dispatcher<TemplateT, TupleT, I, 0>
        {
            static_assert(I != I, "tuple contains insufficient types to satisfy all templates");
        };
        
        } // namespace detail
        
        template<typename TupleT, template<typename...> class... TemplateTs>
        struct template_pack
        {
            static_assert(std::tuple_size<TupleT>::value, "tuple must not be empty");
        
            using type = std::tuple<typename detail::fill_template_dispatcher<
                TemplateTs, TupleT, 0, std::tuple_size<TupleT>::value
            >::type...>;
        };
        
        template<typename TupleT, template<typename...> class... TemplateTs>
        using template_pack_t = typename template_pack<TupleT, TemplateTs...>::type;
        

        Online Demo

        请注意,对于可变参数类模板,这将传递尽可能少的类型(参见测试中的D);对于尽可能传递 许多 类型的实现,请参阅代码here 的变体(在实现中仅更改了两行)。

        【讨论】:

          猜你喜欢
          • 2012-10-27
          • 2021-09-20
          • 2015-01-02
          • 2015-08-22
          • 2010-11-27
          相关资源
          最近更新 更多