使用 std::array 声明 2D(甚至更高维度)数组的便捷方法

Jab*_*cky 42 c++ arrays multidimensional-array stdarray

我即将将大量旧的 C++ 代码转换为更现代的 C++。

该代码中有许多原始的二维数组,例如:

Foo bar[XSIZE][YSIZE];
Run Code Online (Sandbox Code Playgroud)

我即将用以下内容替换这些声明

std::array<std::array<Foo, YSIZE>, XSIZE> bar;
Run Code Online (Sandbox Code Playgroud)

这是一种方便的方法,因为语句保持不变,并且代码的行为应该与原始数组相同,并且具有能够在调试版本中进行越界检查的额外好处。

但在我看来,这std::array<std::array<Foo, YSIZE>>有点麻烦而且不容易阅读,而且如果使用 3D 数组(虽然我没有),情况会更糟。

现在我正在使用这个宏来使声明更具可读性:

#define DECLARE_2D_ARRAY(type, x, y) std::array<std::array<type, y>, x>
...
DECLARE_2D_ARRAY(Foo, XSIZE, YSIZE) bar;
Run Code Online (Sandbox Code Playgroud)

但我觉得这是一个宏黑客,我想知道是否有一种更干净、更 C++ 的方法来做类似的事情。

Ayx*_*xan 76

您可以使用类型别名模板

#include <array> 
#include <cstddef>

template <class T, std::size_t x, std::size_t y>
using Array2D = std::array<std::array<T, y>, x>;

int main() {
    Array2D<int, 5, 3> arr;
}
Run Code Online (Sandbox Code Playgroud)

您还可以将其推广到任何维度:

#include <array>
#include <cstddef>

template <class T, std::size_t size, std::size_t... sizes>
struct ArrayHelper {
    using type = std::array<typename ArrayHelper<T, sizes...>::type, size>;
};

template <class T, std::size_t size>
struct ArrayHelper<T, size> {
    using type = std::array<T, size>;
};

template <class T, std::size_t... sizes>
using Array = typename ArrayHelper<T, sizes...>::type;

int main() { 
    Array<int, 5, 3, 4, 3> arr; 
}
Run Code Online (Sandbox Code Playgroud)


Yak*_*ont 24

template<class A>
struct std_array_helper {
  using type=A;
};

template<class A>
using array_t = typename std_array_helper<A>::type;

template<class T, std::size_t N0>
struct std_array_helper<T[N0]> {
  using type=std::array<array_t<T>, N0>;
};
Run Code Online (Sandbox Code Playgroud)

现在

array_t<Foo[XSIZE][YSIZE]>
Run Code Online (Sandbox Code Playgroud)

std::array< std::array<Foo, XSIZE>, YSIZE>
Run Code Online (Sandbox Code Playgroud)

另一种解决方案是:

template<class T, std::size_t...Sz>
struct array_helper {
  using type=T;
};

template<class T0, std::size_t...Ns>
using array_t = typename array_helper<T0, Ns...>::type;

template<class T, std::size_t N0, std::size_t...Ns>
struct array_helper<T, N0, Ns...>
{
  using type=std::array<array_t<T, Ns...>, N0>;
};
Run Code Online (Sandbox Code Playgroud)

这使用语法:

array_t<Foo, XSIZE, YSIZE>
Run Code Online (Sandbox Code Playgroud)

如果你喜欢的话。

我们甚至可以将两者结合起来,允许任一语法。

template<class T, std::size_t...Sz>
struct array_helper {
  using type=T;
};
template<class T0, std::size_t...Ns>
using array_t = typename array_helper<T0, Ns...>::type;

template<class T, std::size_t N0, std::size_t...Ns>
  requires (!std::is_array_v<T>)
struct array_helper<T, N0, Ns...>
{
  using type = std::array<array_t<T, Ns...>, N0>;
};

template<class T, std::size_t N0, std::size_t...Ns>
struct array_helper<T[N0], Ns...>:
  array_helper<array_t<T, Ns...>, N0>
{};
Run Code Online (Sandbox Code Playgroud)

现在

array_t< Foo[XSIZE], YSIZE >
Run Code Online (Sandbox Code Playgroud)

作品。

但要小心——顺序很棘手!

int[3][2] is an array of 3 elements of arrays of 2 elements.
Run Code Online (Sandbox Code Playgroud)

为了保持我们想要的不变

array_t<int, 3, 2>
Run Code Online (Sandbox Code Playgroud)

成为

std::array< std::array< int, 2 >, 3>
Run Code Online (Sandbox Code Playgroud)

不是

std::array< std::array< int, 3 >, 2>
Run Code Online (Sandbox Code Playgroud)

以下是确定您的顺序是否正确的测试用例:

static_assert( std::is_same_v< std::array<int, 3>, array_t<int, 3> > );
static_assert( std::is_same_v< std::array< std::array<int, 2>, 3>, array_t<int, 3, 2> > );
static_assert( std::is_same_v< std::array< std::array<int, 2>, 3>, array_t<int[3], 2> > );
static_assert( std::is_same_v< std::array< std::array<int, 2>, 3>, array_t<int[3][2]> > );
Run Code Online (Sandbox Code Playgroud)

删除与您选择的语法错误的内容array_t

实例

现在,即使这样也可能是错误的。感觉不正确的是

array_t<int[3], 2>
Run Code Online (Sandbox Code Playgroud)

还没有大小为 3 的子数组

array_t<int[3][2]>
Run Code Online (Sandbox Code Playgroud)

感觉应该也是同一个数组,而且 的布局int[3][2]应该和array_t<int[3][2]>一致array_t<int, 3, 2>

另外,array_t< array_t<int, 3>, 2>应该与 相同array_t<int[3], 2>

这些要求彼此不一致。我的意思是,他们在所有地方都不同意。

解决此问题的最简单方法可能是仅需要[][][]语法,或者不允许混合[]语法,

具有array_t<int[3][2]>相同的布局int[3][2]是高价值的。同样,拥有array_t< int, 3, 2 >语法也具有很高的价值。可能我们想表达的意思与?array_t<int, 3, 2>相同。int[3][2]丢弃这个等于array_t< array_t<int, 3>, 2>- 相反,它等于array_t<array_t<int,2>,3>。最后,块array_t<int[3], 2>语法令人困惑。

然后,拆分array_t< T, 1,2,3,...>模板array_t<T[1][2][3]...>以最大程度地减少混乱。

  • 我喜欢这个解决方案。它非常聪明,尤其是它适用于任何维度。 (2认同)

G. *_*pen 8

在 C++23 中,您可以使用获取一维数组的std::mdspan多维视图。有一个关于 a 的提案std::mdarray,但至少要到 C++26 才会出现在 C++ 中。

\n

std::mdspan如何使用的示例:

\n
std::array<Foo, XSIZE * YSIZE> bar_1d;\nstd::mdspan bar(bar_1d.data(), XSIZE, YSIZE);\n\xe2\x80\xa6\nfor (std::size_t y = 0; y != YSIZE; ++y) {\n    for (std::size_t x = 0; x != XSIZE; ++x) {\n        std::cout << bar[x, y] << \' \';\n    }\n    std::cout << \'\\n\';\n}       \n
Run Code Online (Sandbox Code Playgroud)\n

  • 看起来还没有编译器有完整的 mdspan 实现。 (2认同)