C++20 将多维数组的维度推导到参数包中
C++20 Deducing the dimensions of a multidimensional array into a parameter pack
我正在以多维数组的形式实现一个容器。我正在尝试复制 std::to_array (C++ 20),一个使用我的多维数组从一维内置数组创建 std::array
的函数:
template<typename T, std::size_t... N>
constexpr mdarray<std::remove_cv_t<T>, N...> to_mdarray(T (&a)[N]));
int arr[5][12]{};
to_mdarray(arr); // Returns mdarray<int, 5, 12>
我想将内置数组的维度推导为N
和return对应的mdarray
。我没有弄清楚正确的语法,我总是以错误告终。要么我没有正确展开包,要么编译器认为我正在尝试声明函数指针或 lambda。我也没有在网上找到任何资源。
到目前为止我找到了两个替代方案。一种是将数组衰减为指针并让用户指定维度。
template<typename T, std::size_t... N>
constexpr mdarray<std::remove_cv_t<T>, N...> to_mdarray(T* const a);
int arr[5][12]{};
to_mdarray<5, 12>(arr);
我在 blog from Stanislav Arnaudov 中找到了另一个。它涉及使用递归宏生成第 N 维重载:
#define SIZE_T_S_1 size_t N
#define SIZE_T_S_2 SIZE_T_S_1 , size_t M
#define SIZE_T_S_3 SIZE_T_S_2 , size_t L
#define BRACKETS_S_1 [N]
#define BRACKETS_S_2 BRACKETS_S_1[M]
#define BRACKETS_S_3 BRACKETS_S_2[L]
#define LETTERS_S_1 N
#define LETTERS_S_2 LETTERS_S_1, M
#define LETTERS_S_3 LETTERS_S_2, L
#define TO_MD_N(dim) template<typename T, SIZE_T_S_##dim> \
constexpr mdarray<std::remove_cv_t<T>, LETTERS_S_##dim> to_mdarray(T (&values) BRACKETS_S_##dim);
TO_MD_N(1);
TO_MD_N(2);
TO_MD_N(3);
// Equivalent to:
// TO_MD_N(1)
template<typename T, std::size_t N>
constexpr mdarray<std::remove_cv_t<T>, N> to_mdarray(T (&values)[N]);
// TO_MD_N(2)
template<typename T, std::size_t, std::size_t M>
constexpr mdarray<std::remove_cv_t<T>, N, M> to_mdarray(T (&values)[N][M]);
// TO_MD_N(3)
template<typename T, std::size_t, std::size_t M, std::size_t L>
constexpr mdarray<std::remove_cv_t<T>, N, M, L> to_mdarray(T (&values)[N][M][L]);
这两种解决方案都有缺陷。一种要求用户输入正确的维度,另一种要求多次定义同一个函数并限制维度的个数。
有没有办法在 C++20 中做到这一点,或者目前不可能?
不幸的是,我一贯的 Boost.Mp11 单行趋势将在这里结束,因为 Boost.Mp11 并没有真正很好地处理 values,我们需要将 int[5][12]
转换为 mdarray<int, 5, 12>
并且没有简单的机制可以用那个库来做到这一点。
相反,我们只是手工完成。 std::extent
gives you the N
th extent and std::rank
给出范围数。将它与 make_index_sequence
结合起来,你可以获得所有的 'em:
template <typename T, typename>
struct mdarray_for_impl;
template <typename A, size_t... Is>
struct mdarray_for_impl<A, std::index_sequence<Is...>> {
using type = mdarray<std::remove_all_extents_t<A>, std::extent_v<A, Is>...>;
};
template <typename T>
using mdarray_for = mdarray_for_impl<T, std::make_index_sequence<std::rank_v<T>>>::type;
此处 mdarray_for<int[1][2][3]>
将产生类型 mdarray<int, 1, 2, 3>
.
Boost.Mp11 版本无论如何需要几个辅助别名
// a type-only mdarray
template <typename T, typename... Ns>
using mdarray_t = mdarray<T, Ns::value...>;
// a type-only extent
template <typename T, typename V>
using mp_extent = mp_size_t<std::extent_v<T, V::value>>;
然后允许:
template <typename T>
using mdarray_for2 =
mp_apply<mdarray_t,
mp_append<
mp_list<std::remove_all_extents_t<T>>,
mp_transform_q<
mp_bind<mp_extent, T, _1>,
mp_iota_c<std::rank_v<T>>>
>>;
这与之前的算法相同,除了 mp_iota_c<std::rank_v<T>>
获取范围索引序列(而不是 std::make_index_sequence
)然后 mp_transform_q
获取第 n 个范围(而不是直接在包扩展中使用它)。
在这种情况下,对于 int[5][12]
,我们构建 mp_list<int, mp_size_t<5>, mp_size_t<12>>
(因为我们都是类型,没有值)然后 mp_apply
将其转换为 mdarray_t<...>
它变成 mdarray<int, 5, 12>
.
I would like to deduce the dimensions of the built-in array into N and return the corresponding mdarray.
也许...用一点递归...
template <std::size_t ... Ns, typename T>
constexpr mdarray<std::remove_cv_t<T>, Ns...> get_mdarray_t (T const &)
{ return {}; }
template <std::size_t ... Ns, typename T, std::size_t N>
constexpr auto get_mdarray_t (T const (&arr)[N])
{ return get_mdarray_t<Ns..., N>(arr[0]); }
你可以构建一个有用的using
template <typename T>
using mdarray_t = decltype(get_mdarray_t<>(std::declval<T>()));
以下是完整编译C++11的例子
#include <utility>
template <typename, std::size_t...>
struct mdarray
{ };
template <std::size_t ... Ns, typename T>
constexpr mdarray<std::remove_cv_t<T>, Ns...> get_mdarray_t (T const &)
{ return {}; }
template <std::size_t ... Ns, typename T, std::size_t N>
constexpr auto get_mdarray_t (T const (&arr)[N])
{ return get_mdarray_t<Ns..., N>(arr[0]); }
template <typename T>
using mdarray_t = decltype(get_mdarray_t<>(std::declval<T>()));
int main ()
{
using A0 = int;
using A1 = int[2u];
using A2 = int[2u][3u];
using A3 = int[2u][3u][5u];
using T0 = mdarray_t<A0>;
using T1 = mdarray_t<A1>;
using T2 = mdarray_t<A2>;
using T3 = mdarray_t<A3>;
using U0 = mdarray<int> ;
using U1 = mdarray<int, 2u>;
using U2 = mdarray<int, 2u, 3u>;
using U3 = mdarray<int, 2u, 3u, 5u>;
static_assert( std::is_same<T0, U0>::value, "!" );
static_assert( std::is_same<T1, U1>::value, "!" );
static_assert( std::is_same<T2, U2>::value, "!" );
static_assert( std::is_same<T3, U3>::value, "!" );
}
我正在以多维数组的形式实现一个容器。我正在尝试复制 std::to_array (C++ 20),一个使用我的多维数组从一维内置数组创建 std::array
的函数:
template<typename T, std::size_t... N>
constexpr mdarray<std::remove_cv_t<T>, N...> to_mdarray(T (&a)[N]));
int arr[5][12]{};
to_mdarray(arr); // Returns mdarray<int, 5, 12>
我想将内置数组的维度推导为N
和return对应的mdarray
。我没有弄清楚正确的语法,我总是以错误告终。要么我没有正确展开包,要么编译器认为我正在尝试声明函数指针或 lambda。我也没有在网上找到任何资源。
到目前为止我找到了两个替代方案。一种是将数组衰减为指针并让用户指定维度。
template<typename T, std::size_t... N>
constexpr mdarray<std::remove_cv_t<T>, N...> to_mdarray(T* const a);
int arr[5][12]{};
to_mdarray<5, 12>(arr);
我在 blog from Stanislav Arnaudov 中找到了另一个。它涉及使用递归宏生成第 N 维重载:
#define SIZE_T_S_1 size_t N
#define SIZE_T_S_2 SIZE_T_S_1 , size_t M
#define SIZE_T_S_3 SIZE_T_S_2 , size_t L
#define BRACKETS_S_1 [N]
#define BRACKETS_S_2 BRACKETS_S_1[M]
#define BRACKETS_S_3 BRACKETS_S_2[L]
#define LETTERS_S_1 N
#define LETTERS_S_2 LETTERS_S_1, M
#define LETTERS_S_3 LETTERS_S_2, L
#define TO_MD_N(dim) template<typename T, SIZE_T_S_##dim> \
constexpr mdarray<std::remove_cv_t<T>, LETTERS_S_##dim> to_mdarray(T (&values) BRACKETS_S_##dim);
TO_MD_N(1);
TO_MD_N(2);
TO_MD_N(3);
// Equivalent to:
// TO_MD_N(1)
template<typename T, std::size_t N>
constexpr mdarray<std::remove_cv_t<T>, N> to_mdarray(T (&values)[N]);
// TO_MD_N(2)
template<typename T, std::size_t, std::size_t M>
constexpr mdarray<std::remove_cv_t<T>, N, M> to_mdarray(T (&values)[N][M]);
// TO_MD_N(3)
template<typename T, std::size_t, std::size_t M, std::size_t L>
constexpr mdarray<std::remove_cv_t<T>, N, M, L> to_mdarray(T (&values)[N][M][L]);
这两种解决方案都有缺陷。一种要求用户输入正确的维度,另一种要求多次定义同一个函数并限制维度的个数。
有没有办法在 C++20 中做到这一点,或者目前不可能?
不幸的是,我一贯的 Boost.Mp11 单行趋势将在这里结束,因为 Boost.Mp11 并没有真正很好地处理 values,我们需要将 int[5][12]
转换为 mdarray<int, 5, 12>
并且没有简单的机制可以用那个库来做到这一点。
相反,我们只是手工完成。 std::extent
gives you the N
th extent and std::rank
给出范围数。将它与 make_index_sequence
结合起来,你可以获得所有的 'em:
template <typename T, typename>
struct mdarray_for_impl;
template <typename A, size_t... Is>
struct mdarray_for_impl<A, std::index_sequence<Is...>> {
using type = mdarray<std::remove_all_extents_t<A>, std::extent_v<A, Is>...>;
};
template <typename T>
using mdarray_for = mdarray_for_impl<T, std::make_index_sequence<std::rank_v<T>>>::type;
此处 mdarray_for<int[1][2][3]>
将产生类型 mdarray<int, 1, 2, 3>
.
Boost.Mp11 版本无论如何需要几个辅助别名
// a type-only mdarray
template <typename T, typename... Ns>
using mdarray_t = mdarray<T, Ns::value...>;
// a type-only extent
template <typename T, typename V>
using mp_extent = mp_size_t<std::extent_v<T, V::value>>;
然后允许:
template <typename T>
using mdarray_for2 =
mp_apply<mdarray_t,
mp_append<
mp_list<std::remove_all_extents_t<T>>,
mp_transform_q<
mp_bind<mp_extent, T, _1>,
mp_iota_c<std::rank_v<T>>>
>>;
这与之前的算法相同,除了 mp_iota_c<std::rank_v<T>>
获取范围索引序列(而不是 std::make_index_sequence
)然后 mp_transform_q
获取第 n 个范围(而不是直接在包扩展中使用它)。
在这种情况下,对于 int[5][12]
,我们构建 mp_list<int, mp_size_t<5>, mp_size_t<12>>
(因为我们都是类型,没有值)然后 mp_apply
将其转换为 mdarray_t<...>
它变成 mdarray<int, 5, 12>
.
I would like to deduce the dimensions of the built-in array into N and return the corresponding mdarray.
也许...用一点递归...
template <std::size_t ... Ns, typename T>
constexpr mdarray<std::remove_cv_t<T>, Ns...> get_mdarray_t (T const &)
{ return {}; }
template <std::size_t ... Ns, typename T, std::size_t N>
constexpr auto get_mdarray_t (T const (&arr)[N])
{ return get_mdarray_t<Ns..., N>(arr[0]); }
你可以构建一个有用的using
template <typename T>
using mdarray_t = decltype(get_mdarray_t<>(std::declval<T>()));
以下是完整编译C++11的例子
#include <utility>
template <typename, std::size_t...>
struct mdarray
{ };
template <std::size_t ... Ns, typename T>
constexpr mdarray<std::remove_cv_t<T>, Ns...> get_mdarray_t (T const &)
{ return {}; }
template <std::size_t ... Ns, typename T, std::size_t N>
constexpr auto get_mdarray_t (T const (&arr)[N])
{ return get_mdarray_t<Ns..., N>(arr[0]); }
template <typename T>
using mdarray_t = decltype(get_mdarray_t<>(std::declval<T>()));
int main ()
{
using A0 = int;
using A1 = int[2u];
using A2 = int[2u][3u];
using A3 = int[2u][3u][5u];
using T0 = mdarray_t<A0>;
using T1 = mdarray_t<A1>;
using T2 = mdarray_t<A2>;
using T3 = mdarray_t<A3>;
using U0 = mdarray<int> ;
using U1 = mdarray<int, 2u>;
using U2 = mdarray<int, 2u, 3u>;
using U3 = mdarray<int, 2u, 3u, 5u>;
static_assert( std::is_same<T0, U0>::value, "!" );
static_assert( std::is_same<T1, U1>::value, "!" );
static_assert( std::is_same<T2, U2>::value, "!" );
static_assert( std::is_same<T3, U3>::value, "!" );
}