是否可以交流参数包

Is it possible to interleave parameter packs?

本文关键字:参数 是否      更新时间:2023-10-16

有可能使用C 11/14/1z,具有带有两个参数包的模板构造,其中,它可以用两个包装的两个包装来实例化其他模板构造?即,如果第一个包是T1_1,T1_2,T1_3,而第二个包是T2_1,T2_2和T2_3,则交织包将为T1_1,T2_1,T1_2,T2_2,T2_2,T1_3,T1_3,T2_3,T2_3,T2_3?我的猜测是"否",因为省略号不应适用于两个参数包元素指定器。但是,也许某种元组建设或递归建筑技巧可以起作用?

编辑:假设包装具有相等的长度(sizeof...值),让我明确说明。如果不会使事情变得更加令人费解,则可以解决各种长度的解决方案。

使用 tuple_cat是过度的。更不用说不必要地约束了。

Trivial Pack类:

template<class...> struct pack {};

一个变色的concat,写得很小:

template<class T = pack<>, class...> 
struct concat { using type = T; };
template<class... T1, class... T2, class... Ts>
struct concat<pack<T1...>, pack<T2...>, Ts...> 
    : concat<pack<T1..., T2...>, Ts...> {};
template<class... Ts> 
using concat_t = typename concat<Ts...>::type;

然后,交织本身同样微不足道 - 扩展到两种类型的包装中,然后将它们加入:

template<class... Us>
struct interleave {
     template<class... Vs>
     using with = concat_t<pack<Us, Vs>...>;
};

演示。

实际上已经添加到标准中。我现在无法自己测试,但是这个想法应该起作用。

template <class Tuple1, class Tuple2, std::size_t ... indices>
auto interleave(Tuple1 t1, Tuple2 t2, std::integer_sequence<std::size_t, indices...>)
{
    return std::tuple_cat(std::make_tuple(std::get<indices>(t1),
                                          std::get<indices>(t2))...);
}

template <class Tuple1, class Tuple2>
auto interleave(Tuple1 t1, Tuple2 t2)
{
    return interleave(t1, t2, std::make_index_sequence<std::tuple_size<Tuple1>::value>());
}

带有功能和 decltype s,您可以轻松地做到这一点。
这是一个工作示例:

#include<tuple>
template<std::size_t... I>
constexpr auto f(std::index_sequence<I...>, auto tup1, auto tup2) {
    return std::tuple_cat(std::make_tuple(std::get<I>(tup1), std::get<I>(tup2))...);
}
template<typename... V>
struct S {
    template<typename... U, std::enable_if_t<(sizeof...(V) == sizeof...(U))>* = nullptr>
    static auto generate() {
        return f(std::make_index_sequence<sizeof...(U)>(), std::tuple<V...>{}, std::tuple<U...>{});
    }
};
int main() {
    static_assert(
        std::is_same<
            decltype(S<int, double>::template generate<char, void*>()),
            std::tuple<int, char, double, void*>
        >::value,
        "!"
    );
}

确保可能。您可以通过包装器获得两个不同的包装:

template < typename L0, typename L1 >
struct interleave;
template < typename ... Pack >
struct pack {};
template < typename ... P0, typename ... P1 >
struct interleave<pack<P0...>, pack<P1...>>
{
   using type = ???;
};
#include <tuple>
#include <iostream>
#include <utility>
#include <typeinfo>
template<class T1, class T2>
struct interleave
{
  static constexpr std::size_t size = std::tuple_size<T1>::value;
  static_assert(size == std::tuple_size<T2>::value, ""); 
  template<class T> struct impl;
  template<std::size_t...Is>
  struct impl<std::index_sequence<Is...>>
  {
    using type = std::tuple
      <
      std::tuple<std::tuple_element_t<Is, T1>,
      std::tuple_element_t<Is, T2>>...
      >;
  };
  template<class T> struct dedup;
  template<class...Ts>
    struct dedup<std::tuple<Ts...>>
    {
      using type = decltype(std::tuple_cat(std::declval<Ts>()...)); 
    };
  using dups = typename impl<decltype(std::make_index_sequence<size>())>::type;
  using type = typename dedup<dups>::type;
};

int main()
{
  using t = interleave<std::tuple<int, char, float>, std::tuple<unsigned, double, const char*>>::type;
  std::cout << typeid(t).name() << std::endl;
}

aim:

获得类型的别名:

Foo<int, double, char, const char*>

使用2个参数包:

using Pack1 = Pack<int, char>;
using Pack2 = Pack<double, const char*>;

然后交织在一起:

typename ToFoo<Pack1, Pack2>::type

并执行它们等效的static_assert

using T1 = Foo<int, double, char, const char*>;
using T2 = typename ToFoo<Pack<int, char>, Pack<double, const char*>>::type;
static_assert(std::is_same<T1, T2>::value, "passed");

解决方案:

我们可以将2个元素交织成1个,如下所示:

template <class Tuple1, class Tuple2, std::size_t ... indices>
auto interleave(Tuple1 t1, Tuple2 t2, std::integer_sequence<std::size_t, indices...>)
{
    return std::tuple_cat(std::make_tuple(std::get<indices>(t1),
                                          std::get<indices>(t2))...);
}
template <class Tuple1, class Tuple2>
auto interleave(Tuple1 t1, Tuple2 t2)
{
    return interleave(t1, t2, std::make_index_sequence<std::tuple_size<Tuple1>::value>());
}

我们可以按照以下方式获取所得交织的元组的类型:

template<typename... Ts>
struct Pack 
{
    using type = std::tuple<Ts...>;
};
template<typename T0, typename T1>
struct Interleaved;
template<typename... P0, typename... P1>
struct Interleaved<Pack<P0...>, Pack<P1...>>
{
    using Pack0 = typename Pack<P0...>::type;
    using Pack1 = typename Pack<P1...>::type;
    using type = decltype(interleave(std::declval<Pack0>(), std::declval<Pack1>()));
};

然后,我们可以采用该std::tuple<Ts...>类型,然后将其"转换"到Foo<Ts...>,如下所示:

template<typename T>
struct TupleToFoo;
template<typename... Ts>
struct TupleToFoo<std::tuple<Ts...>>
{
    using type = Foo<Ts...>;
};

最后,我们将其全部包装在一个辅助类ToFoo上,该类别为2 Packs并定义类型的别名:

template<typename T0, typename T1>
struct ToFoo;
template<typename... P0, typename... P1>
struct ToFoo<Pack<P0...>, Pack<P1...>>
{
    using type = typename TupleToFoo<typename Interleaved<Pack<int, char>, Pack<double, const char*>>::type>::type;
};

完整的工作示例:(coliru)

#include <tuple>
template <class Tuple1, class Tuple2, std::size_t ... indices>
auto interleave(Tuple1 t1, Tuple2 t2, std::integer_sequence<std::size_t, indices...>)
{
    return std::tuple_cat(std::make_tuple(std::get<indices>(t1),
                                          std::get<indices>(t2))...);
}
template <class Tuple1, class Tuple2>
auto interleave(Tuple1 t1, Tuple2 t2)
{
    return interleave(t1, t2, std::make_index_sequence<std::tuple_size<Tuple1>::value>());
}
template<typename... Ts>
struct Pack 
{
    using type = std::tuple<Ts...>;
};
template<typename T0, typename T1>
struct Interleaved;
template<typename... P0, typename... P1>
struct Interleaved<Pack<P0...>, Pack<P1...>>
{
    using Pack0 = typename Pack<P0...>::type;
    using Pack1 = typename Pack<P1...>::type;
    using type = decltype(interleave(std::declval<Pack0>(), std::declval<Pack1>()));
};
template<typename... Ts>
struct Foo 
{};
template<typename T>
struct TupleToFoo;
template<typename... Ts>
struct TupleToFoo<std::tuple<Ts...>>
{
    using type = Foo<Ts...>;
};
template<typename T0, typename T1>
struct ToFoo;
template<typename... P0, typename... P1>
struct ToFoo<Pack<P0...>, Pack<P1...>>
{
    using type = typename TupleToFoo<typename Interleaved<Pack<int, char>, Pack<double, const char*>>::type>::type;
};

int main()
{
    using T1 = Foo<int, double, char, const char*>;
    using T2 = typename ToFoo<Pack<int, char>, Pack<double, const char*>>::type;
    static_assert(std::is_same<T1, T2>::value, "passed");
    return 0;
}