【问题标题】:Create a type list combination of types in C++在 C++ 中创建类型的类型列表组合
【发布时间】:2019-07-27 01:17:09
【问题描述】:

我正在尝试创建一些工具来创建基于其他类型组合的类型列表。

假设我们有三种类型

struct A{};
struct B{};
struct C{};

我想获得一个元组列表,其中包含 N 种类型 A、B 或 C 的所有可能组合。

对于 N=2 的情况,这将是

std::tuple<A,A>
std::tuple<A,B>
std::tuple<A,C>
std::tuple<B,A>
std::tuple<B,B>
std::tuple<B,C>
std::tuple<C,A>
std::tuple<C,B>
std::tuple<C,C>

我们的想法是创建一个元组,其中包含所有这些类型的容器,以便我以后可以将任何这些类型存储在容器列表中。

template <typename ...Combinations>
using CombinationList = std::tuple<std::vector<Combinations>...>;

我已经有一种机制可以在它适合的容器中插入一个 particupar 元素,但我不知道如何创建组合。

在 cmets 上,人们建议使用 std::vector&lt;Combination&lt;std::variant&lt;A,C,B&gt;, std::variant&lt;A,B,C&gt;&gt;&gt;。尽管这在技术上解决了问题,但我不想使用它,因为 A、B C 的大小非常不同,我不想在运行时访问变体。此外,在某些时候,我需要上传容器中的所有数据

std::tuple<std::vector<Combination>...>

到 GPU,所以我不能在这里使用 std::variant。

我该怎么做?

谢谢!

PD:这与这个问题有关Combination explosion of an enum value (729 combinations...) 在那个问题中,我问我如何轻松生成将进入容器的类型。现在我需要生成容器。

【问题讨论】:

  • std::variant (en.cppreference.com/w/cpp/utility/variant) 的工作量会少很多。
  • 你会怎么做?我不确定如何使用变体来实现...
  • 使用std::vector&lt;std::tuple&lt;std::variant&lt;A, B, C&gt;, std::variant&lt;A, B, C&gt;&gt;&gt;之类的东西怎么样?
  • A、B 和 C 可以有非常不同的大小。这是针对物理模拟器的,在引导期间我不关心性能,但在模拟期间我(非常)关心它。这就是为什么我不喜欢使用这样的变体。另外,在某个时候,我会将所有数据上传到 GPU
  • 您不能创建包含不同类型对象的向量。 C++ 中没有这样的东西。您的选择是 (1) 变体向量或 (2) 指向某些(抽象)基类的(智能)指针的向量,而派生类负责具体类型。

标签: c++ c++14 variadic-templates template-meta-programming cartesian-product


【解决方案1】:

我已经有一种机制可以在它适合的容器中插入一个 particupar 元素,但我不知道如何创建组合。

假设你有一个类型列表(比如A, B, C)和一个无符号整数N,我建议一个using

template <std::size_t N, typename ... Ts>
using Combinations = ???

定义为std::tuple,其中包含std::tuples 的所有组合列表。

举个例子,

Combinations<2u, A, B, C>

成为

  std::tuple<
      std::tuple<A,A>, std::tuple<A,B>, std::tuple<A,C>,
      std::tuple<B,A>, std::tuple<B,B>, std::tuple<B,C>,
      std::tuple<C,A>, std::tuple<C,B>, std::tuple<C,C>>

以下是一个完整的C++11编译示例

#include <tuple>
#include <vector>
#include <type_traits>

struct A {};
struct B {};
struct C {};

template <typename T, typename ... Ts>
constexpr std::tuple<T, Ts...> addTupleType (std::tuple<Ts...>);

template <typename T, typename ... Ts>
constexpr auto addType ()
   -> std::tuple<decltype(addTupleType<T>(std::declval<Ts>()))...>;

template <typename ... Ts, typename ... Us>
constexpr auto getCombinations (std::integral_constant<std::size_t, 0u>,
                                std::tuple<Ts...> t, std::tuple<Us ...> u)
   -> decltype( u );

template <std::size_t N, typename ... Ts, typename ... Us,
          typename std::enable_if<(N > 0u), bool>::type = true>
constexpr auto getCombinations (std::integral_constant<std::size_t, N>,
                                std::tuple<Ts...> t, std::tuple<Us ...>)
   -> decltype (getCombinations(
         std::integral_constant<std::size_t, N-1u>{}, t,
         std::tuple_cat(addType<Ts, Us...>()...)));

template <std::size_t N, typename ... Ts>
using Combinations
   = decltype(getCombinations(
         std::integral_constant<std::size_t, N-1u>{},
         std::declval<std::tuple<Ts...>>(),
         std::declval<std::tuple<std::tuple<Ts>...>>()));

template <typename ... Ts>
constexpr auto CombListHelper (std::tuple<Ts...>)
   -> std::tuple<std::vector<Ts>...>;

template <typename T>
using CombinationList = decltype(CombListHelper(std::declval<T>()));


int main()
 {
   using type_1 = Combinations<2u, A, B, C>;
   using type_2 = std::tuple<
      std::tuple<A,A>, std::tuple<A,B>, std::tuple<A,C>,
      std::tuple<B,A>, std::tuple<B,B>, std::tuple<B,C>,
      std::tuple<C,A>, std::tuple<C,B>, std::tuple<C,C>>;

   static_assert( std::is_same<type_1, type_2>::value, "!" );

   using type_3 = CombinationList<Combinations<2u, A, B, C>>;
   using type_4 = std::tuple<
      std::vector<std::tuple<A,A>>, std::vector<std::tuple<A,B>>,
      std::vector<std::tuple<A,C>>, std::vector<std::tuple<B,A>>,
      std::vector<std::tuple<B,B>>, std::vector<std::tuple<B,C>>,
      std::vector<std::tuple<C,A>>, std::vector<std::tuple<C,B>>,
      std::vector<std::tuple<C,C>>>;

   static_assert( std::is_same<type_3, type_4>::value, "!" );
 }

【讨论】:

  • 虽然不能完全解决我的问题,但我想我可以以此为起点。现在我只需要为每个可能的组合生成一个向量。非常感谢!
  • @jjcasmar - 不确定你到底想要什么,但是......答案通过CombinationList 得到改善(请参阅type_3main() 中的type_4
  • 哇,令人印象深刻。这正是我想要的。非常感谢!
  • typename std::enable_if&lt;(N &gt; 0u), bool&gt;::type = true 似乎不需要。与std::integral_constant&lt;std::size_t, 0u&gt; 重载会更好。
  • 而且,由于只有声明,constexpr 也不需要。
【解决方案2】:

使用将二维矩阵存储在线性存储中的类比,ABC 的所有可能对都由一维整数 0,1,...,8 标记,如下所示:

0 -> (0/3, 0%3) = (0,0) -> std::tuple<A,A>
1 -> (1/3, 1%3) = (0,1) -> std::tuple<A,B>
...
8 -> (8/3, 8%3) = (2,2) -> std::tuple<C,C>

因此我们可以构造如下的对列表。 这些函数在 C++14 及更高版本中工作。 例如,Combinations&lt;A,B,C&gt;::types 等于 std::tuple&lt;std::vector&lt;std::tuple&lt;A,A&gt;&gt;, std::vector&lt;std::tuple&lt;A,B&gt;&gt;, ...&gt;

Live DEMO

template<std::size_t I, typename Tuple>
struct make_pair_vector
{
    static constexpr std::size_t  left_index = I/std::tuple_size<Tuple>::value;
    static constexpr std::size_t right_index = I%std::tuple_size<Tuple>::value;

    using type = std::vector<
                    std::tuple<typename std::tuple_element< left_index, Tuple>::type,
                               typename std::tuple_element<right_index, Tuple>::type>>;
};

template <typename T, typename Is>
struct make_combinations;

template <typename Tuple, std::size_t... Is>
struct make_combinations<Tuple, std::index_sequence<Is...>>
{
    using tuples = std::tuple<typename make_pair_vector<Is, Tuple>::type...>;
};

template<typename ...Args>
struct Combinations
{
    using types = typename make_combinations
                    <std::tuple<Args...>,
                     std::make_index_sequence<(sizeof...(Args))*(sizeof...(Args))>>
                    ::tuples;
};

【讨论】:

  • 这是一个不错的尝试,但它仅适用于两个元素(AA、AB、AC、AB)的组合,但不适用于三个元素(AAA、ABB、AAB...)谢谢无论如何,你是一个非常好的代码。
  • 除法和取模来选择类型...有趣的想法。
  • @jjcasmar(和 max66),谢谢。我希望你会顺利。顺便说一句,将这种方法推广到三元组或更大的维度可能并不难。
  • @Hiroki 好主意。我可以编辑您对一般情况的回答吗?这是我的建议:wandbox.org/permlink/aAQ3ZJX73IfViZax.
  • @Julius 啊,好代码!但是我想保持最新的答案,因为简单地说,那不是我的代码:)。不用说,您可以将其发布为您自己的答案。无论如何,谢谢你,干得好!
猜你喜欢
  • 1970-01-01
  • 2012-10-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多