简体   繁体   中英

Cross multiply non-type variadic templates

My goal is to make this template work:

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

So I can use it like this:

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

Because I need to make this:

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

Here's a solution that uses static_assert not only to prove the order of the cross-product, but the entire cross product, explicitly.

#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;
}

If you want compile time computation, write constexpr functions

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...>());

They're easier to read, write and looks more or less the same as normal functions.

Use as

cross_tuple_t<Ns...> cross_arrays;

Not exactly as you asked...

For the cross type I suppose you can use a standard std::index_sequence .

To construct the correct sequence, I see a simple (?) recursive solution, based on an mult_helper helper struct.

#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>, "!" );
 }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM