展平一堆类型,其中非类型值是展平的一部分 [英] Flattening a pack of types, where non-type values are part of the flattening

查看:74
本文介绍了展平一堆类型,其中非类型值是展平的一部分的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果您查看以下成员的类型

If you look at the member type of

template <typename Pack> struct flatten;

template <typename T, typename U, std::size_t A, std::size_t B, std::size_t C, typename V, std::size_t D, std::size_t E, typename W>
struct flatten<std::tuple<T,U, std::index_sequence<A,B,C>, V, std::index_sequence<D,E>, W>> {
    template <typename, typename, std::size_t, std::size_t, std::size_t, typename, std::size_t, std::size_t, typename> struct S;
    using type = S<T,U,A,B,C,V,D,E,W>;
};

是否可以通用地执行此操作,因此将所有类型和非类型均压扁为单个压扁的包装,只要将非类型包装在类型中?

is it possible to do this generically, thus flattening all types and non-types into a single flattened pack, as long as the non-types are wrapped within a type?

这是我对普通展平类型的简单解决方案,我只是想知道如何使用

Here is my simple solution to the normal type of flattening, and I'm just wondering how to use this method to work the above generically.

template <typename T> struct identity { using type = T; };

template <typename...> struct merge;

template <typename Pack>
struct merge<Pack> : identity<Pack> {};

template <template <typename...> class P, typename... Ts, typename... Us>
struct merge<P<Ts...>, P<Us...>> : identity<P<Ts..., Us...>> {};

template <typename First, typename... Rest>
struct merge<First, Rest...> : merge<First, typename merge<Rest...>::type> {};

template <typename T, template <typename...> class P> struct flatten_h : identity<P<T>> {};

template <template <typename...> class P, typename... Ts>
struct flatten_h<P<Ts...>, P> : merge<typename flatten_h<Ts, P>::type...> {};   

template <typename Pack> struct flatten;

template <template <typename...> class P, typename... Ts>
struct flatten<P<Ts...>> : flatten_h<P<Ts...>, P> {};

// Testing
#include <type_traits>

template <typename...> struct P;

int main() {
    static_assert(std::is_same<
        flatten<P<int, char, long, P<double, bool>, int>>::type,
        P<int, char, long, double, bool, int>
    >::value);

    static_assert(std::is_same<
        flatten<P<int, P<bool, bool>, long, P<double, P<char, P<long>>>, int>>::type,
        P<int, bool, bool, long, double, char, long, int>
    >::value);
}

我认为C ++ 20应该允许 auto。 .. (或其他关键字)来指示模板参数的类型和非类型值。

I think C++20 should allow auto... (or some other keyword) to indicate both types and non-type values for template arguments.

推荐答案

如果每个非类型参数(例如,从 std :: index_sequence 解包的参数)可以包装在自己的中,则该任务变得可行std :: integral_constant 。然后,在发生展平的级别上只有类型模板参数,并且可以使用简单的类型容器,例如 template< class ...>。 struct Types {};

The task becomes feasible if each non-type parameter (unpacked from, say, an std::index_sequence) can be wrapped in its own std::integral_constant. Then there are only type template parameters at the level where the flattening occurs and one may use a simple type container like template<class...> struct Types {};.

// upper version for shorter type names; lower version for showing types
template<auto v> struct Val : std::integral_constant<decltype(v), v> {};
//template<auto v> using Val = std::integral_constant<decltype(v), v>;
// NOTE: bug(?) in GCC 7.2 which maps size_t(0) to false and size_t(1) to true

template<class I, I... is>
auto flatten(Type< std::integer_sequence<I, is...> >) {
  return Flattened<Val<is>...>{};
}

下面和在实时演示中,您可以找到一个完整的工作示例,其中包含该限制(以及注释中提到的bug(?))。

Below and in a live demo you find a full working example with that restriction (and the bug(?) mentioned in the comment).

我选择指定所有应该展平的类型。或者,也可以使用 template< template< template< auto,class ...>形式的各种模板模板参数盲目地解压缩一切合理的东西。 class ToFlatten> 等。

I chose to specify all types which should be flattened. Alternatively, one may also blindly unpack "everything reasonable" using various template template arguments of the form template<template<auto, class...> class ToFlatten> etc.

#include <iostream>
#include <tuple>
#include <utility>

// upper version for shorter type names; lower version for showing types
template<auto v> struct Val : std::integral_constant<decltype(v), v> {};
//template<auto v> using Val = std::integral_constant<decltype(v), v>;
// NOTE: bug(?) in GCC 7.2 which maps size_t(0) to false and size_t(1) to true

template<size_t... is, class F>
constexpr decltype(auto) indexer_impl(std::index_sequence<is...>, F f) {
  return f(Val<is>{}...);
}

template<size_t size, class F>
constexpr decltype(auto) indexer(F f) {
  return indexer_impl(std::make_index_sequence<size>{}, f);
}

////////////////////////////////////////////////////////////////////////////////

template<class T_>
struct Type {};

template<class... Ts>
struct Types {
  static constexpr auto size = Val<sizeof...(Ts)>{};
  using Tuple = std::tuple<Ts...>;
};

template<size_t i, class T>
using at_t = std::tuple_element_t<i, typename T::Tuple>;

////////////////////////////////////////////////////////////////////////////////

template<class...> struct Flattened;

template<class I, I... is> using int_seq = std::integer_sequence<I, is...>;

// specify which types are allowed in a flat type container
template<class T> struct is_flat : Val<true> {};
template<class I, I... is> struct is_flat< int_seq<I, is...> > : Val<false> {};
template<class... Ts> struct is_flat< Types<Ts...> > : Val<false> {};
template<class... Ts> struct is_flat< Flattened<Ts...> > : Val<false> {};

// check if a type is an instantiation of `template<class...> struct Flattened`
template<class T> struct is_flattened : Val<false> {};
template<class... Ts> struct is_flattened<Flattened<Ts...>> : Val<true> {};

// specific type container which guarantees to contain `is_flat` types only
template<class... Ts> struct Flattened : Types<Ts...> {
  static_assert((... && is_flat<Ts>{}));
};

////////////////////////////////////////////////////////////////////////////////

namespace internal {

auto merge() {
  return Flattened<>{};
}

template<class... Ts>
auto merge(Flattened<Ts...> done) {
  return done;
}

template<class... Ts, class... Us>
auto merge(Flattened<Ts...>, Flattened<Us...>) {
  return Flattened<Ts..., Us...>{};
}

// merge more than two args: attempt to avoid linear recursion: is it better?
template<class... Ts, class... Fs>
auto merge(Flattened<Ts...>, Fs...) {
  static_assert((... && is_flattened<Fs>{}));

  using T = Types<Flattened<Ts...>, Fs...>;

// group the Flattened args into two halves
  constexpr size_t N = T::size;
  constexpr size_t N0 = N/2u;
  constexpr size_t N1 = N-N0;

  auto h0 = indexer<N0>([] (auto... is) { return merge(at_t<is, T>{}...); });
  auto h1 = indexer<N1>([] (auto... is) { return merge(at_t<N0+is, T>{}...); });

  return merge(h0, h1);
}

template<class T>
auto flatten(Type<T>) {
  static_assert(is_flat<T>{});
  return Flattened<T>{};
}

template<class I, I... is>
auto flatten(Type< std::integer_sequence<I, is...> >) {
  return Flattened<Val<is>...>{};
}

template<class... Ts>
auto flatten(Type< Types<Ts...> >) {
  return merge(internal::flatten(Type<Ts>{})...);
}

}// internal

template<class... Ts>
auto flatten(Types<Ts...>) {
  return internal::merge(internal::flatten(Type<Ts>{})...);
}

////////////////////////////////////////////////////////////////////////////////

template<class T>
void inspect() {
  std::cout << __PRETTY_FUNCTION__ << std::endl;
}

struct Custom {};

int main() {
  auto foo = Types<
    Types<int, char, long>,
    Val<7>,
    Types<double, Val<5>, float, Types<unsigned, Types<Custom, Types<char>, int>>, std::make_index_sequence<4u>>,
    std::index_sequence<5u,19u,4u>,
    Types<>,
    Val<8>
  >{};

  auto bar = flatten(foo);
  inspect<decltype(bar)>();

  return 0;
}

输出:

void inspect() [with T = Flattened<int, char, long int, Val<7>, double, Val<5>, float, unsigned int, Custom, char, int, Val<false>, Val<true>, Val<2>, Val<3>, Val<5>, Val<19>, Val<4>, Val<8> >]

带有较长类型名称的输出:

Output with longer type names:

void inspect() [with T = Flattened<int, char, long int, std::integral_constant<int, 7>, double, std::integral_constant<int, 5>, float, unsigned int, Custom, char, int, std::integral_constant<bool, false>, std::integral_constant<bool, true>, std::integral_constant<long unsigned int, 2>, std::integral_constant<long unsigned int, 3>, std::integral_constant<int, 5>, std::integral_constant<long unsigned int, 19>, std::integral_constant<long unsigned int, 4>, std::integral_constant<int, 8> >]

这篇关于展平一堆类型,其中非类型值是展平的一部分的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆