交叉乘法非类型可变参数模板

Cross multiply non-type variadic templates

我的目标是让这个模板发挥作用:

template <size_t... Ns>
struct mult
{
    using cross = ?; // variadic size_t
    static_assert(sizeof...(cross) + 1 == sizeof...(Ns), "");
};

所以我可以这样使用它:

mult<2,3,5>::cross // 6,15 // because 2*3=6, 3*5=15
mult<3,5,7,11>::cross // 15,35,77 // because 3*5=15, 5*7=35, 7*11=77

因为我需要做这个:

// tuple of arrays
std::tuple<std::array<size_t, mult<Ns...>::cross>...> cross_arrays;

这里有一个解决方案,它使用 static_assert 不仅证明叉积的顺序,而且显式证明整个叉积。

#include <type_traits>
#include <utility>
#include <cstddef>

template<size_t ...Ns> struct indices;

template<typename, typename> struct add_indices;

template<size_t ...N1, size_t ...N2>
struct add_indices<indices<N1...>, indices<N2...>> {

    typedef indices<N1..., N2...> equals;
};


template<size_t ...Ns> struct cross_impl;

template<size_t N1, size_t N2> struct cross_impl<N1, N2> {

    typedef indices<N1 * N2> result;
};

template<size_t N1, size_t N2, size_t N3, size_t ...Ns>
struct cross_impl<N1, N2, N3, Ns...> {

    typedef typename add_indices< indices<N1 * N2>,
                 typename cross_impl<N2, N3, Ns...>::result
                      >::equals result;
};

template<size_t ...Ns>
struct mult
{
    using cross=typename cross_impl<Ns...>::result;
};

int main() {

    static_assert(std::is_same<typename mult<2,3,5>::cross,
              indices<6,15>>::value);

    static_assert(std::is_same<typename mult<3,5,7,11>::cross,
              indices<15,35,77>>::value);

    return 0;
}

和你问的不完全一样...

对于 cross 类型,我想你可以使用标准的 std::index_sequence

为了构建正确的序列,我看到了一个基于 mult_helper 辅助结构的简单(?)递归解决方案。

#include <utility>
#include <iostream>

template <typename, std::size_t ...>
struct mult_helper;

template <std::size_t A0, std::size_t A1, std::size_t ... As,
          std::size_t ... Ns>
struct mult_helper<std::index_sequence<A0, A1, As...>, Ns...>
   : public mult_helper<std::index_sequence<A1, As...>, Ns..., A0*A1>
 { };

template <std::size_t A0, std::size_t ... Ns>
struct mult_helper<std::index_sequence<A0>, Ns...>
 { using type = std::index_sequence<Ns...>; };

template <std::size_t... Ns>
struct mult
{ using cross = typename mult_helper<std::index_sequence<Ns...>>::type; };

int main ()
 {
   using T1 = mult<3u, 5u, 7u, 11u>::cross;
   using T2 = std::index_sequence<15u, 35u, 77u>;

   static_assert( std::is_same_v<T1, T2>, "!" );
 }

如果要编译时计算,写constexpr函数

template<typename... SizeT>
constexpr auto cross(size_t x, SizeT... xs)
{
    std::array tmp = {x, xs...};
    std::array ret = {xs...};
    for(size_t i = 0; i < ret.size(); i++)
        ret[i] *= tmp[i];
    return ret;
}

template<size_t... xs, size_t... Is>
auto cross_tuple_fn(std::index_sequence<Is...>)
{
    constexpr auto dims = cross(xs...);
    return std::tuple<std::array<size_t, dims[Is]>...>{};
}

template<size_t... xs>
auto cross_tuple_fn()
{
    return cross_tuple_fn<xs...>(std::make_index_sequence<sizeof...(xs) - 1>{});
}

template<size_t... xs>
using cross_tuple_t = decltype(cross_tuple_fn<xs...>());

它们更易于阅读、编写,并且看起来与普通函数大致相同。

用作

cross_tuple_t<Ns...> cross_arrays;