【问题标题】:C++20 Deducing the dimensions of a multidimensional array into a parameter packC++20 将多维数组的维度推导出为参数包
【发布时间】:2021-07-04 19:19:02
【问题描述】:

我正在以多维数组的形式实现一个容器。我正在尝试复制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,并返回对应的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 中有什么方法可以做到这一点,还是目前不可能?

【问题讨论】:

标签: c++ arrays variadic-templates c++20


【解决方案1】:

不幸的是,我通常的 Boost.Mp11 单线趋势将在这里结束,因为 Boost.Mp11 并不能很好地处理 values,我们需要将 int[5][12] 转换为mdarray&lt;int, 5, 12&gt; 并且没有简单的机制可以使用该库来做到这一点。

相反,我们只是手工完成。 std::extent 为您提供Nth 范围,std::rank 为您提供范围的数量。将其与make_index_sequence 结合起来,您就可以获得全部:

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&lt;int[1][2][3]&gt; 将产生mdarray&lt;int, 1, 2, 3&gt; 类型。


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&lt;std::rank_v&lt;T&gt;&gt; 获取范围索引序列(而不是std::make_index_sequence),然后mp_transform_q 获取第n 个范围(而不是直接在包扩展中使用它) .

在这种情况下,对于int[5][12],我们构建mp_list&lt;int, mp_size_t&lt;5&gt;, mp_size_t&lt;12&gt;&gt;(因为我们都是类型,没有值),然后mp_apply 将其转换为mdarray_t&lt;...&gt;,然后将其转换为mdarray&lt;int, 5, 12&gt;

【讨论】:

    【解决方案2】:

    我想将内置数组的维数推导出为N,并返回对应的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, "!" );
     }
    

    【讨论】:

      猜你喜欢
      • 2013-04-24
      • 1970-01-01
      • 1970-01-01
      • 2013-06-29
      • 2016-08-14
      • 1970-01-01
      • 1970-01-01
      • 2018-03-06
      • 1970-01-01
      相关资源
      最近更新 更多