扩展包含模板

时间:2016-05-13 23:51:08

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

定义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, "");
}

如何概括以上?它甚至可能吗?

3 个答案:

答案 0 :(得分: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 = /* ... */;

这问了问题&#34; Z<Ts...>有效吗?如果是,请返回true_type&#34;。

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

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

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

这需要一个模板Z和一个types<Ts...>,然后返回Z<Ts...>

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

这会找到types的最长前缀,可以有效地传递给Z::template result<???>。有些工作是为了避免传递太短的序列,以防它们失败&#34;迟到&#34;:

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_apply位于many of my posts

pop_front是微不足道的。

apply应该很容易。

答案 1 :(得分:2)

这是&#34;找到最短的前缀&#34;。

公用设施:

// 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 :(得分:1)

这看起来很有趣所以我试着看了它;我没有声称这比现有答案更好,但也许有些人会觉得更清楚:

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的变体(在实现中仅更改了两行)。