优雅地声明 2(甚至多)维 std::arrays
Declaring 2 (or even multi-) dimensional std::arrays elegantly
我正在使用基于 std::array
的二维数组。
基本上代替:
MyType myarray[X_SIZE][Y_SIZE];
我有:
std::array<std::array<MyType, Y_SIZE>, X_SIZE> myarray;
这工作得很好,但 IMO 声明的可读性不是很好。
有没有办法使用一些聪明的 C++ 模板机制来声明它,这样声明看起来像这样?
My2DArray<Mytype, X_SIZE, Y_SIZE> myarray;
如果您只需要二维数组,这非常简单:
template <class T, std::size_t X, std::size_t Y>
using My2DArray = std::array<std::array<T, Y>, X>;
如果你想要一个不限于二维数组的通用机制,也可以做到:
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%
的参数和折叠表达式的参数。
请注意,如果类型 T
在关联的命名空间中具有不受约束的 operator%
,这将 运行 出现问题(请限制您的运算符!)。我们可以通过选择不太可能的运算符来降低发生这种情况的风险,例如 .*
、->*
或 %=
;或者我们可以使用 array_type<T>
包装器。这两种解决方案都没有完全避免 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>
。
解释:
std:rank
给出数组类型的维数。因此,对于 int[4][5][6]
,它 returns 3.
- 我们将其交给
make_index_sequence
以得到一组索引。 (0, 1, 2
)
std::remove_all_extents
给出了数组的底层类型; T[a][b]...[n]
-> T
(int
)
std::extent
给了我们给定维度的范围。我们为每个索引调用它。 (4, 5, 6
).
通过将这些传递给我们之前实现的 md_array_t
,我们最终得到 md_array_t<int, 4, 5, 6>
,它会产生我们想要的结果。
我正在使用基于 std::array
的二维数组。
基本上代替:
MyType myarray[X_SIZE][Y_SIZE];
我有:
std::array<std::array<MyType, Y_SIZE>, X_SIZE> myarray;
这工作得很好,但 IMO 声明的可读性不是很好。
有没有办法使用一些聪明的 C++ 模板机制来声明它,这样声明看起来像这样?
My2DArray<Mytype, X_SIZE, Y_SIZE> myarray;
如果您只需要二维数组,这非常简单:
template <class T, std::size_t X, std::size_t Y>
using My2DArray = std::array<std::array<T, Y>, X>;
如果你想要一个不限于二维数组的通用机制,也可以做到:
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%
的参数和折叠表达式的参数。
请注意,如果类型 T
在关联的命名空间中具有不受约束的 operator%
,这将 运行 出现问题(请限制您的运算符!)。我们可以通过选择不太可能的运算符来降低发生这种情况的风险,例如 .*
、->*
或 %=
;或者我们可以使用 array_type<T>
包装器。这两种解决方案都没有完全避免 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>
。
解释:
std:rank
给出数组类型的维数。因此,对于int[4][5][6]
,它 returns 3.- 我们将其交给
make_index_sequence
以得到一组索引。 (0, 1, 2
) std::remove_all_extents
给出了数组的底层类型;T[a][b]...[n]
->T
(int
)std::extent
给了我们给定维度的范围。我们为每个索引调用它。 (4, 5, 6
).
通过将这些传递给我们之前实现的 md_array_t
,我们最终得到 md_array_t<int, 4, 5, 6>
,它会产生我们想要的结果。