交叉乘法非类型可变参数模板
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;
我的目标是让这个模板发挥作用:
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;