优雅地声明 2 维(甚至多维)标准::数组

Declaring 2 (or even multi-) dimensional std::arrays elegantly

本文关键字:标准 数组 声明      更新时间:2023-10-16

我正在使用基于std::array的二维数组。

基本上而不是:

MyType myarray[X_SIZE][Y_SIZE];

我有:

std::array<std::array<MyType, Y_SIZE>, X_SIZE> myarray;

这工作得很好,但 IMO 声明的可读性不是很好。

有没有办法使用一些聪明C++模板 mecanism 来声明这一点,所以声明看起来像这样?

My2DArray<Mytype, X_SIZE, Y_SIZE> myarray;

如果你只想要2D数组,那就相当简单了:

template <class T, std::size_t X, std::size_t Y>
using My2DArray = std::array<std::array<T, Y>, X>;

如果您想要一个不限于 2D 数组的通用机制,也可以这样做:

template <class T, std::size_t N, std::size_t... Ns>
struct AddArray {
using type = std::array<typename AddArray<T, Ns...>::type, N>;
};
template <class T, std::size_t N>
struct AddArray<T, N> {
using type = std::array<T, N>;
};
template <class T, std::size_t... N>
using MyNDArray = typename AddArray<T, N...>::type;

[现场示例]

实现此操作的一种优雅方法是使用折叠表达式:

// Some namespace to hide the poorly-constrained template function:
namespace array_making {
template <std::size_t N>
struct array_dim {};
template <typename T, std::size_t N>
constexpr auto operator%(array_dim<N>, T const&)
-> std::array<T, N>;
}
template <typename T, std::size_t... Is>
using md_array_t = decltype(
(array_making::array_dim<Is>{} % ... % std::declval<T>())
);

编译器资源管理器。

那么md_array_t<int, 1, 2, 3>array<array<array<int, 3>, 2>, 1>了.如果您更喜欢相反的顺序,请将operator%参数和参数反转为 fold 表达式。


请注意,如果类型T在关联的命名空间中具有不受约束的operator%,这将遇到问题(请约束您的运算符!我们可以通过选择不太可能的运算符(如.*->*%=(来降低发生这种情况的风险;或者我们可以使用array_type<T>包装器。这两种解决方案都不能完全避免操作员过载受到不当约束的问题,T

我们可以包装现有的MyNDArray/md_array_t答案之一以达到替代接口:

template <typename Arr, std::size_t... Is>
constexpr auto make_array_impl(std::index_sequence<Is...>)
-> md_array_t<std::remove_all_extents_t<Arr>,
std::extent_v<Arr, Is>...>;
template <typename Arr>
using make_array = decltype(make_array_impl<Arr>(
std::make_index_sequence<std::rank_v<Arr>>{}));

编译器资源管理器

这使我们能够将make_array<int[4][5][6]>写成表示array<array<array<int, 6>, 5, 4>.


解释:

  1. std:rank给出数组类型的维数。因此,对于int[4][5][6],它返回 3。
  2. 我们把它交给make_index_sequence,最终得到一包索引。(0, 1, 2(
  3. std::remove_all_extents为我们提供了数组的底层类型;T[a][b]...[n]->T(int(
  4. std::extent给了我们给定维度的范围。我们对每个索引都调用此名称。(4, 5, 6(。

通过将这些传递给我们之前实现的md_array_t,我们最终得到md_array_t<int, 4, 5, 6>,它产生我们想要的东西。