如何在C ++中创建类型列表的笛卡尔乘积方法?


26

自我解释。

基本上说我有这样的类型列表:

using type_list_1 = type_list<int, somestructA>;
using type_list_2 = type_list<somestructB>;
using type_list_3 = type_list<double, short>;

它们可以是各种类型的类型列表。

如何获得笛卡尔积的类型列表?

result = type_list<
type_list<int, somestructB, double>,
type_list<int, somestructB, short>,
type_list<somestructA, somestructB, double>,
type_list<somestructA, somestructB, short>
>;

我确实涉足如何创建如下所示的双向笛卡尔乘积:如何创建类型列表的笛卡尔乘积?,但n方法似乎并不那么琐碎。

现在我正在尝试...

template <typename...> struct type_list{};

// To concatenate
template <typename... Ts, typename... Us>
constexpr auto operator|(type_list<Ts...>, type_list<Us...>) {
   return type_list{Ts{}..., Us{}...};
}

template <typename T, typename... Ts, typename... Us>
constexpr auto cross_product_two(type_list<T, Ts...>, type_list<Us...>) {
    return (type_list<type_list<T,Us>...>{} | ... | type_list<type_list<Ts, Us>...>{});
}

template <typename T, typename U, typename... Ts>
constexpr auto cross_product_impl() {
    if constexpr(sizeof...(Ts) >0) {
        return cross_product_impl<decltype(cross_product_two(T{}, U{})), Ts...>();
    } else {
        return cross_product_two(T{}, U{});
    }
}

我要说的是,考虑到正确实现的难度,请像Barry的回答那样使用boost。不幸的是,我必须坚持使用手动方法,因为是否使用增强是来自其他地方的决定:(


8
OOF,你惩罚😏贪食
亮度种族在轨道

我有点不满意,但是您可以通过以下方式修改2维笛卡尔积:1)第一个类型列表实际上是一种类型的类型列表的类型列表;2)不是将类型列表中的两个类型串联在一起,元函数会将第二个列表中的类型附加到第一个类型列表的“子”列表中(以笛卡尔乘积的方式)?如果可行,则可以使用递归算法轻松解决该问题。
smitsyn

1
递归实现的真正困难在于,它cartesian_product是类型列表的列表,并且在每个递归步骤中,您都希望将内容附加到每个内部类型列表中。进入包装的第二个包装级别需要一些扣除...
Max Langhof

1
我猜您也可以通过将其视为要遍历每个“类型网格点”的N维“类型空间”来“线性地”实现它。计算网格点的数量,然后像遍历展平的ND数组一样遍历网格点,并计算每个网格点的类型。需要考虑的事情……
Max Langhof

1
@MaxLanghof类似于“ C ++ 17中元组的笛卡尔积 ”吗?
Deduplicator

Answers:


14

使用Boost.Mp11,这是一小段(一如既往):

using result = mp_product<
    type_list,
    type_list_1, type_list_2, type_list_3>;

演示


1
神圣的牛...但是我不得不指出,(在godbolt上对每个代码进行多次采样)Mp11版本的编译时间大约是原来的两倍。不知道有多少开销在解析boost报头本身,还有多少在实例化模板...
Max Langhof

1
@MaxLanghof当然。如果仅包括algorithm.hpp而不是全部Mp11,则为1.5倍。即使如此,我们还是说的是0.08s和0.12s。还必须考虑我也花了多长时间编写了这份文件。
巴里

8
@Barry:从软件工程的角度来看,与您100%在一起。与手动滚动方法相比,这也很容易阅读。另外,几乎不需要测试就可以确保磁带库解决方案的正确性。总体而言,更少的代码和更高的信心将减少其终生维护成本。
AndyG '19

我同意这很简单,但不幸的是,有些团队对此不满意。
themagicalyang '19

有些团队对此一无所知。这不是不使用它的原因。
Tomaz Canabrava

13

好的,我知道了。它不是很漂亮,但是可以工作:

template<class ... T>
struct type_list{};

struct somestructA{};
struct somestructB{};

using type_list_1 = type_list<int, somestructA, char>;
using type_list_2 = type_list<somestructB>;
using type_list_3 = type_list<double, short, float>;

template<class TL1, class TL2>
struct add;

template<class ... T1s, class ... T2s>
struct add<type_list<T1s...>, type_list<T2s...>>
{
    using type = type_list<T1s..., T2s...>;
};

template<class ... TL>
struct concat;

template<class TL, class ... TLs>
struct concat<TL, TLs...>
{
    using type = typename add<TL, typename concat<TLs...>::type>::type;
};

template<class TL>
struct concat<TL>
{
    using type = TL;
};

static_assert(std::is_same_v<type_list<int, somestructA, char, double, short, float>, typename add<type_list_1, type_list_3>::type>);

template<class TL1, class TL2>
struct multiply_one;

// Prepends each element of T1 to the list T2.
template<class ... T1s, class ... T2s>
struct multiply_one<type_list<T1s...>, type_list<T2s...>>
{
    using type = typename concat<type_list<type_list<T1s, T2s...>...>>::type;
};

static_assert(std::is_same_v<
    type_list<
        type_list<int, double, short, float>,
        type_list<somestructA, double, short, float>,
        type_list<char, double, short, float>
        >,
    typename multiply_one<type_list_1, type_list_3>::type>);

// Prepends each element of TL to all type lists in TLL.
template<class TL, class TLL>
struct multiply_all;

template<class TL, class ... TLs>
struct multiply_all<TL, type_list<TLs...>>
{
    using type = typename concat<typename multiply_one<TL, TLs>::type...>::type;
};

static_assert(std::is_same_v<
    type_list<
        type_list<int, double, short, float>,
        type_list<somestructA, double, short, float>,
        type_list<char, double, short, float>
        >,
    typename multiply_all<type_list_1, type_list<type_list_3>>::type>);

static_assert(std::is_same_v<
    type_list<
        type_list<int, somestructB>,
        type_list<somestructA, somestructB>,
        type_list<char, somestructB>,
        type_list<int, double, short, float>,
        type_list<somestructA, double, short, float>,
        type_list<char, double, short, float>
        >,
    typename multiply_all<type_list_1, type_list<type_list_2, type_list_3>>::type>);

template<class TL, class ... TLs>
struct cartesian_product
{
    using type = typename multiply_all<TL, typename cartesian_product<TLs...>::type>::type;
};

template<class ... Ts>
struct cartesian_product<type_list<Ts...>>
{
    using type = type_list<type_list<Ts>...>;
};


using expected_result = type_list<
    type_list<int, somestructB, double>,
    type_list<somestructA, somestructB, double>,
    type_list<char, somestructB, double>,
    type_list<int, somestructB, short>,
    type_list<somestructA, somestructB, short>,
    type_list<char, somestructB, short>,
    type_list<int, somestructB, float>,
    type_list<somestructA, somestructB, float>,
    type_list<char, somestructB, float>
>;

static_assert(std::is_same_v<expected_result,
    cartesian_product<type_list_1, type_list_2, type_list_3>::type>);

https://godbolt.org/z/L5eamT

我把自己的static_assert测试留在那里...好吧,希望他们能帮上忙。

另外,我敢肯定必须有一个更好的解决方案。但这是显而易见的“我知道最终将达到目标”的道路。我最终不得不求助于a concat或sorts,我敢肯定,它可以更早地用于跳过大部分内容。


4
我可以遵循的模板编程。棒极了。我今天学到了一些东西。
杰里·耶利米

add需要两个type_lists。如何传递多个类型列表以添加concat?
themagicalyang '19

@themagicalyang很好地发现了,这是一个错误(测试未发现cos所有涉及的列表仅是长度2)。在...已去递归内concat通话,而不是外界。答案(包括测试用例)已更正。在正确性期望方面证明Barry的权利:)
Max Langhof

笛卡尔乘积不是将乘数全部设为乘数一吗?
themagicalyang '19

@themagicalyang No. cartesian_product实现递归。为包中的每个类型列表multiply_all执行一个。是类型列表的列表。需要一个类型列表和一个类型列表列表。有两个类型的列表,并与创建,,。你需要扣除额(这两个层面,),因为你需要向下降落“variadicness”两个层面,看我对这个问题的第一个评论。multiply_oneTLscartesian_product::typemultiply_allmultiply_onea1, a2, a3b1, b2, b3a1, b1, b2, b3a2, b1, b2, b3a3, b1, b2, b3multiply_allmultiply_one
Max Langhof

9

折叠表情再次解救

template<typename... Ts>
typelist<typelist<Ts>...> layered(typelist<Ts...>);

template<typename... Ts, typename... Us>
auto operator+(typelist<Ts...>, typelist<Us...>)
    -> typelist<Ts..., Us...>;

template<typename T, typename... Us>
auto operator*(typelist<T>, typelist<Us...>)
    -> typelist<decltype(T{} + Us{})...>;

template<typename... Ts, typename TL>
auto operator^(typelist<Ts...>, TL tl)
    -> decltype(((typelist<Ts>{} * tl) + ...));

template<typename... TLs>
using product_t = decltype((layered(TLs{}) ^ ...));

这样就完成了。与具有O(1)实例化深度的递归相比,这具有其他好处。

struct A0;
struct A1;
struct B0;
struct B1;
struct C0;
struct C1;
struct C2;

using t1 = typelist<A0, A1>;
using t2 = typelist<B0, B1>;
using t3 = typelist<C0, C1, C2>; 

using p1 = product_t<t1, t2>;
using p2 = product_t<t1, t2, t3>;

using expect1 = typelist<typelist<A0, B0>,
                         typelist<A0, B1>,
                         typelist<A1, B0>,
                         typelist<A1, B1>>;

using expect2 = typelist<typelist<A0, B0, C0>,
                         typelist<A0, B0, C1>,
                         typelist<A0, B0, C2>,
                         typelist<A0, B1, C0>,
                         typelist<A0, B1, C1>,
                         typelist<A0, B1, C2>,
                         typelist<A1, B0, C0>,
                         typelist<A1, B0, C1>,
                         typelist<A1, B0, C2>,
                         typelist<A1, B1, C0>,
                         typelist<A1, B1, C1>,
                         typelist<A1, B1, C2>>;

static_assert(std::is_same_v<p1, expect1>);
static_assert(std::is_same_v<p2, expect2>);

这引起了我的兴趣。有没有办法将其表示为TL1 * TL2 * TL3 =交叉孔结果?
themagicalyang '19

@themagicalyang“跨乘积结果”是什么意思?
过客在

基本上代替using result = product_t<t1,t2,t3>...以某种方式将其表示为using result = decltype(t1{} * t2{} * t3{});。嗯,既然已经考虑到了,既然decltype不可避免,只需使用您提供的别名就更直观了。
themagicalyang '19

有趣!使用运算符重载为您提供折叠表达式,而不是我必须执行的递归。也使其更加简洁。下次我会记住的!
Max Langhof

@PasserBy是否所有这些辅助运算符和函数都必须位于同一命名空间中?我将所有内容放入命名空间并使用来自外部命名空间的别名访问product_t时遇到问题。
themagicalyang '19
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.