提问人:Jabberwocky 提问时间:8/8/2023 最后编辑:journpyJabberwocky 更新时间:8/28/2023 访问量:3058
使用 std::array 声明 2D(甚至更高维度)数组的便捷方法
Convenient way to declare 2D (or even higher dimension) arrays with std::array
问:
我即将将许多旧的 C++ 代码转换为更现代的 C++。
该代码中有许多原始的 2D 数组,例如:
Foo bar[XSIZE][YSIZE];
我将用
std::array<std::array<Foo, YSIZE>, XSIZE> bar;
这是一种方便的方法,因为语句保持不变,代码的行为应该与原始数组相同,并且能够在调试版本中进行越界检查。
但是 IMO 有点麻烦且不容易阅读,而使用 3D 阵列(尽管我没有)甚至会更糟。std::array<std::array<Foo, YSIZE>>
现在我正在使用这个宏来使声明更具可读性:
#define DECLARE_2D_ARRAY(type, x, y) std::array<std::array<type, y>, x>
...
DECLARE_2D_ARRAY(Foo, XSIZE, YSIZE) bar;
但我觉得这是一个宏黑客,我想知道是否有更干净、更 C++ 的方法来做类似的事情。
答:
您可以使用类型别名模板:
#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;
}
您也可以将其概括为任何维度:
#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;
}
评论
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>;
};
现在
array_t<Foo[XSIZE][YSIZE]>
是
std::array< std::array<Foo, XSIZE>, YSIZE>
另一种解决方案是:
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>;
};
这使用以下语法:
array_t<Foo, XSIZE, YSIZE>
如果你喜欢它。
我们甚至可以将两者结合起来,允许任何一种语法。
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>
{};
现在
array_t< Foo[XSIZE], YSIZE >
工程。
但要小心 - 订单很棘手!
int[3][2] is an array of 3 elements of arrays of 2 elements.
为了保持我们想要的相同
array_t<int, 3, 2>
成为
std::array< std::array< int, 2 >, 3>
不
std::array< std::array< int, 3 >, 2>
以下是用于确定订单是否正确的测试用例:
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]> > );
删除对您选择的语法错误的 .array_t
现在,即使这样也可能是错误的。感觉不对
array_t<int[3], 2>
还没有大小为 3 的子数组
array_t<int[3][2]>
感觉也应该是同一个数组,布局应该和一致。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>
然后,拆分 from 模板以最大程度地减少混淆。array_t< T, 1,2,3,...>
array_t<T[1][2][3]...>
评论
在 C++23 中,可以使用 std::mdspan
获取一维数组的多维视图。有一个关于std::mdarray
的建议,但至少在C++26之前不会出现在C++中。
如何使用的示例:std::mdspan
std::array<Foo, XSIZE * YSIZE> bar_1d;
std::mdspan bar(bar_1d.data(), XSIZE, YSIZE);
…
for (std::size_t y = 0; y != YSIZE; ++y) {
for (std::size_t x = 0; x != XSIZE; ++x) {
std::cout << bar[x, y] << ' ';
}
std::cout << '\n';
}
评论