创建一个用 0,1,2,3, 元组初始化的多维数组
Creating a multi-dimensional array initialized with tuples of 0,1,2,3,
函数createMultiArray<M,N>()
创建一个
`std::array<std::array<std::tuple<std::size_t, std::size_t>, M>, N>`,
其元素是:
(0, 0) (0, 1) (0, 2) (0, 3)
(1, 0) (1, 1) (1, 2) (1, 3)
(2, 0) (2, 1) (2, 2) (2, 3)
这是我的简单实现:
#include <iostream>
#include <tuple>
#include <utility>
template <std::size_t M, std::size_t N>
struct InitializeMultiArray {
using Array = std::array<std::array<std::tuple<std::size_t, std::size_t>, M>, N>;
template <std::size_t... Is>
static Array execute (std::index_sequence<Is...>) {
Array array;
const int a[] = {(initialize<Is>(array, std::make_index_sequence<M>{}), 0)...};
static_cast<void>(a);
return array;
}
private:
template <std::size_t I, std::size_t... Is>
static void initialize (Array& array, std::index_sequence<Is...>) {
const int a[] = {(array[I][Is] = std::make_tuple(I, Is), 0)...};
static_cast<void>(a);
}
};
template <std::size_t M, std::size_t N>
std::array<std::array<std::tuple<std::size_t, std::size_t>, M>, N> createMultiArray() {
return InitializeMultiArray<M,N>::execute(std::make_index_sequence<N>{});
}
int main() {
constexpr std::size_t M = 4, N = 3;
const std::array<std::array<std::tuple<std::size_t, std::size_t>, M>, N> array = createMultiArray<M,N>();
for (std::size_t i = 0; i < N; i++) {
for (std::size_t j = 0; j < M; j++)
std::cout << "(" << std::get<0>(array[i][j]) << ", " << std::get<1>(array[i][j]) << ") ";
std::cout << 'n';
}
}
现在我需要将createMultiArray<M,N>()
扩展到任意数量的维度createMultiArray<Dimensions...>()
,这样array[i][j][k]...[last] = std::make_tuple(i,j,k,...,last)
. 我被困在如何进行这种概括上。 谁能在这里帮忙?
以下是存储在多维数组中的精确元组类型:
template <std::size_t N>
using Type = std::size_t;
template <typename> struct TupleOfIntsHelper;
template <std::size_t... Is>
struct TupleOfIntsHelper<std::index_sequence<Is...>> {
using type = std::tuple<Type<Is>...>;
};
template <std::size_t N>
using TupleOfInts = typename TupleOfIntsHelper<std::make_index_sequence<N>>::type;
// ...
static_assert (std::is_same<TupleOfInts<3>, std::tuple<std::size_t, std::size_t, std::size_t>>::value, "");
则createMultiArray<Dimensions...>()
的返回类型为
typename NArray<TupleOfInts<sizeof...(Dimensions)>, Dimensions...>::type
哪里
template <typename, std::size_t...> struct NArray;
template <typename T, std::size_t N>
struct NArray<T,N> {
using type = std::array<T,N>;
};
template <typename T, std::size_t First, std::size_t... Rest>
struct NArray<T, First, Rest...> {
using type = std::array<typename NArray<T, Rest...>::type, First>;
};
所以唯一困难的任务是如上所述初始化它:
template <std::size_t... Dimensions>
typename NArray<TupleOfInts<sizeof...(Dimensions)>, Dimensions...>::type createMultiArray() {
typename NArray<TupleOfInts<sizeof...(Dimensions)>, Dimensions...>::type array;
// ???
return array;
}
更新:这是我的一个想法:
template <typename... IndexSequences>
struct AllCombinations {
using type = std::tuple<std::index_sequence<0,0,0>, std::index_sequence<0,0,1>>; // etc...
// Generate these based on IndexSequences...
};
template <typename Combinations, typename Array>
void initialize (Array& array) {
// Use each type in Combinations to initialize 'array' via a function like
// void initialize_impl(Array& array, std::index_sequence<Is...>) {
// get_array_element(array, {Is...}) = std::make_tuple(Is...);
// }
}
template <std::size_t... Dimensions>
typename NArray<TupleOfInts<sizeof...(Dimensions)>, Dimensions...>::type createMultiArray() {
typename NArray<TupleOfInts<sizeof...(Dimensions)>, Dimensions...>::type array;
using Combinations = typename AllCombinations<std::make_index_sequence<Dimensions>...>::type;
initialize<Combinations>(array);
return array;
}
这是我上面提到的initialize_impl
函数:
template <std::size_t I>
struct MultiArrayGet {
template <typename Array, std::size_t N>
static auto& get (Array& a, const std::array<std::size_t, N>& index) {
return MultiArrayGet<I - 1>::get(a[index[N - I]], index); // Here I is just a counter so that we know when to stop.
}
};
template <>
struct MultiArrayGet<0> {
template <typename T, std::size_t N>
static auto& get (T& t, const std::array<std::size_t, N>&) { return t; }
};
template <std::size_t N, typename Array>
auto& get_array_element (Array& a, const std::array<std::size_t, N>& index) {
return MultiArrayGet<N>::get(a, index);
}
template <typename Array, std::size_t... Is>
void initialize_impl (Array& array, std::index_sequence<Is...>) {
get_array_element<sizeof...(Is)>(array, {Is...}) = std::make_tuple(Is...);
}
我们不要让事情变得不必要的复杂。从概念上讲,您要编写的初始化只是一堆嵌套的for
循环:
for(std::size_t i = 0; i < Dim0; ++i)
for(std::size_t j = 0; j < Dim1; ++j)
for(std::size_t k = 0; k < Dim2; ++k)
// ...
for(std::size_t last = 0; last < DimN; ++last)
array[i][j][k]...[last] = std::make_tuple(i,j,k,...,last);
所以让我们这样做。 这是一个简单的递归。
namespace details {
template<class... Ts, class... Args>
void init_array(std::tuple<Ts...>& tup, Args... args) {
static_assert(sizeof...(Ts) == sizeof...(args), "Oops");
tup = std::make_tuple(args...);
}
template<class Array, class... Args>
void init_array(Array& arr, Args... args) {
for(std::size_t i = 0; i < arr.size(); ++i){
init_array(arr[i], args..., i);
}
}
}
template <std::size_t... Dimensions>
typename NArray<TupleOfInts<sizeof...(Dimensions)>, Dimensions...>::type createMultiArray() {
typename NArray<TupleOfInts<sizeof...(Dimensions)>, Dimensions...>::type array;
details::init_array(array);
return array;
}
这可以在第 17 C++constexpr
。
对于那些真正想要C++14 constexpr
的人来说,这并不难。创建数组后,我们无法将其索引到数组中,因此需要在初始化中完成。
namespace details {
// create an std::array out of the provided elements
template<class... Ts>
constexpr std::array<std::common_type_t<Ts...>, sizeof...(Ts)> make_array(Ts&&... ts) {
return { { std::forward<Ts>(ts)... } };
}
// terminating case just creates a tuple.
template<std::size_t... Dimensions, class... Ts>
constexpr auto createMultiArrayHelper(std::index_sequence<Dimensions...>,
std::index_sequence<>, Ts... vals){
static_assert(sizeof...(Dimensions) == sizeof...(vals), "Oops");
return std::make_tuple(vals...);
}
template<std::size_t... Dimensions, std::size_t... Is, class... Ts>
constexpr auto createMultiArrayHelper(std::index_sequence<Dimensions...>,
std::index_sequence<Is...>, Ts... vals){
constexpr std::size_t dims[] = {Dimensions..., 0}; // 0 for the terminating case
constexpr auto next_dim = dims[sizeof...(vals) + 1];
return make_array(createMultiArrayHelper(std::index_sequence<Dimensions...>(),
std::make_index_sequence<next_dim>(), vals..., Is)...);
}
}
template<std::size_t... Dimensions>
constexpr auto createMultiArray(){
constexpr std::size_t dims[] = {Dimensions...};
return details::createMultiArrayHelper(std::index_sequence<Dimensions...>(),
std::make_index_sequence<dims[0]>());
}
将这C++11 constexpr
留给读者作为练习。
在这里,我相信 T.C. 的 C++14 constexpr 解决方案尽可能缩短,并尽可能优化(因为传递的参数更少(:
#include <iostream>
#include <tuple>
#include <utility>
template <typename... Ts>
constexpr std::array<std::common_type_t<Ts...>, sizeof...(Ts)> makeArray (Ts&&... ts) {
return { {std::forward<Ts>(ts)...} };
}
template <typename... Ts>
constexpr auto createMultiArrayHelper (std::index_sequence<>, Ts... vals) {
return std::make_tuple(vals...);
}
template <std::size_t First, std::size_t... Rest, std::size_t... Is, typename... Ts>
constexpr auto createMultiArrayHelper (std::index_sequence<Is...>, Ts... vals) {
return makeArray (createMultiArrayHelper<Rest...>(std::make_index_sequence<First>{}, vals..., Is)...);
}
template <std::size_t First, std::size_t... Rest>
constexpr auto createMultiArray() {
return createMultiArrayHelper<Rest..., 0>(std::make_index_sequence<First>{});
}
// Testing
int main() {
constexpr std::size_t M = 3, N = 2, P = 4;
constexpr auto array = createMultiArray<M,N,P>();
for (std::size_t i = 0; i < M; i++) {
for (std::size_t j = 0; j < N; j++) {
for (std::size_t k = 0; k < P; k++)
std::cout << "(" << std::get<0>(array[i][j][k]) << ", " << std::get<1>(array[i][j][k]) << ", " << std::get<2>(array[i][j][k]) << ") ";
std::cout << 'n';
}
}
}
输出:
(0, 0, 0) (0, 0, 1) (0, 0, 2) (0, 0, 3)
(0, 1, 0) (0, 1, 1) (0, 1, 2) (0, 1, 3)
(1, 0, 0) (1, 0, 1) (1, 0, 2) (1, 0, 3)
(1, 1, 0) (1, 1, 1) (1, 1, 2) (1, 1, 3)
(2, 0, 0) (2, 0, 1) (2, 0, 2) (2, 0, 3)
(2, 1, 0) (2, 1, 1) (2, 1, 2) (2, 1, 3)
由于这里的每个 constexpr 函数都已经由单个返回行组成,因此它实际上已经是一个 C++11 解决方案(只需要定义 std::index_sequence 并添加尾随的 decltype 返回类型(。
好的,我想我得到了一个可行的解决方案。 明天我会努力改进它。 使用 std::index_sequence
实际上给我带来了烦人的 bug,因为它只是 std::integer_sequence<std::size_t, Is...>
的别名,不适用于我的 ExpandPacks
类(我稍后会尝试将它们放回去(,所以我从第一原则定义了sequence
和make_sequence
并改用它们。
#include <iostream>
#include <tuple>
#include <utility>
template <std::size_t...> struct sequence {};
template <std::size_t N, std::size_t... Is>
struct make_sequence_helper : make_sequence_helper<N-1, N-1, Is...> {};
template <std::size_t... Is>
struct make_sequence_helper<0, Is...> {
using type = sequence<Is...>;
};
template <std::size_t N>
using make_sequence = typename make_sequence_helper<N>::type;
template <typename, std::size_t... Dimensions> struct NArray;
template <typename T, std::size_t N>
struct NArray<T,N> {
using type = std::array<T,N>;
};
template <typename T, std::size_t First, std::size_t... Rest>
struct NArray<T, First, Rest...> {
using type = std::array<typename NArray<T, Rest...>::type, First>;
};
template <std::size_t N>
using Type = std::size_t;
template <typename> struct TupleOfIntsHelper;
template <std::size_t... Is>
struct TupleOfIntsHelper<std::index_sequence<Is...>> {
using type = std::tuple<Type<Is>...>;
};
template <std::size_t N>
using TupleOfInts = typename TupleOfIntsHelper<std::make_index_sequence<N>>::type;
template <std::size_t I, typename Pack> struct Prepend;
template <typename...> struct Merge;
template <std::size_t I, template <std::size_t...> class P, std::size_t... Is>
struct Prepend<I, P<Is...>> {
using type = P<I, Is...>;
};
template <typename Pack>
struct Merge<Pack> {
using type = Pack;
};
template <template <typename...> class P, typename... Ts, typename... Us>
struct Merge<P<Ts...>, P<Us...>> {
using type = P<Ts..., Us...>;
};
template <typename First, typename... Rest>
struct Merge<First, Rest...> : Merge<First, typename Merge<Rest...>::type> {};
template <typename... Packs> struct ExpandPacks;
template <std::size_t I, typename Pack> struct PairEach;
template <std::size_t I, typename PackOfPacks> struct PrependEach;
template <typename Pack, typename PackOfPacks> struct ExpandPacksHelper;
template <template <std::size_t...> class P, std::size_t I, std::size_t... Is>
struct PairEach<I, P<Is...>> {
using type = std::tuple<P<I, Is>...>;
};
template <std::size_t I, typename... Packs>
struct PrependEach<I, std::tuple<Packs...>> {
using type = std::tuple<typename Prepend<I, Packs>::type...>;
};
template <template <std::size_t...> class P, std::size_t... Is, typename... Packs>
struct ExpandPacksHelper<P<Is...>, std::tuple<Packs...>> : Merge<typename PrependEach<Is, std::tuple<Packs...>>::type...> {};
template <template <std::size_t...> class P, std::size_t... Is, typename Pack>
struct ExpandPacks<P<Is...>, Pack> : Merge<typename PairEach<Is, Pack>::type...> {};
template <typename First, typename... Rest>
struct ExpandPacks<First, Rest...> : ExpandPacksHelper<First, typename ExpandPacks<Rest...>::type> {};
template <std::size_t I>
struct MultiArrayGet {
template <typename Array, std::size_t N>
static auto& get (Array& a, const std::array<std::size_t, N>& index) {
return MultiArrayGet<I - 1>::get(a[index[N - I]], index); // Here I is just a counter so that we know when to stop.
}
};
template <>
struct MultiArrayGet<0> {
template <typename T, std::size_t N>
static auto& get (T& t, const std::array<std::size_t, N>&) { return t; }
};
template <std::size_t N, typename Array>
auto& get_array_element (Array& a, const std::array<std::size_t, N>& index) {
return MultiArrayGet<N>::get(a, index);
}
template <typename Array, std::size_t... Is>
void initialize_impl (Array& array, sequence<Is...>) {
get_array_element<sizeof...(Is)>(array, {Is...}) = std::make_tuple(Is...);
}
template <typename Combinations> struct Initialize;
template <template <typename...> class P>
struct Initialize<P<>> {
template <typename Array>
static void execute (Array&) {} // End of recursion.
};
template <template <typename...> class P, typename First, typename... Rest>
struct Initialize<P<First, Rest...>> {
template <typename Array>
static void execute (Array& array) {
initialize_impl (array, First{});
Initialize<P<Rest...>>::execute(array);
}
};
template <std::size_t... Dimensions>
typename NArray<TupleOfInts<sizeof...(Dimensions)>, Dimensions...>::type createMultiArray() {
typename NArray<TupleOfInts<sizeof...(Dimensions)>, Dimensions...>::type array;
using Combinations = typename ExpandPacks<make_sequence<Dimensions>...>::type;
Initialize<Combinations>::execute(array);
return array;
}
int main() {
constexpr std::size_t M = 3, N = 2, P = 4;
const auto array = createMultiArray<M,N,P>(); // 'auto' is std::array<std::array<std::array<std::tuple<std::size_t, std::size_t, std::size_t>, P>, N>, M>.
for (std::size_t i = 0; i < M; i++) {
for (std::size_t j = 0; j < N; j++) {
for (std::size_t k = 0; k < P; k++)
std::cout << "(" << std::get<0>(array[i][j][k]) << ", " << std::get<1>(array[i][j][k]) << ", " << std::get<2>(array[i][j][k]) << ") ";
std::cout << 'n';
}
}
}
输出:
(0, 0, 0) (0, 0, 1) (0, 0, 2) (0, 0, 3)
(0, 1, 0) (0, 1, 1) (0, 1, 2) (0, 1, 3)
(1, 0, 0) (1, 0, 1) (1, 0, 2) (1, 0, 3)
(1, 1, 0) (1, 1, 1) (1, 1, 2) (1, 1, 3)
(2, 0, 0) (2, 0, 1) (2, 0, 2) (2, 0, 3)
(2, 1, 0) (2, 1, 1) (2, 1, 2) (2, 1, 3)
不过,可能有一个更短的解决方案。 我会尝试改进这个丑陋的解决方案,但至少我让它工作了。 欢迎更好的解决方案。
- 是否可以在编译时初始化数组,以便在运行时不会花费时间?
- 在函数内部的声明中初始化数组,并在外部使用它
- 为什么用结构初始化数组需要指定结构名称
- 有没有一种代码密度较低的方法来使用非默认构造函数初始化数组?
- C++使用另一个数组和新值初始化数组
- 初始化数组、"memset"或" {//value} "的最佳方法是什么?
- C1001内部编译器错误是由于矢量初始化(如数组)引起的
- 在 constexpr 构造函数中初始化数组是否合法?
- 我可以初始化 const 实例,以便我可以将其用作 const 来初始化数组吗?
- 在构造函数中初始化数组
- 是否可以使用函数返回的值初始化数组
- 使用宏使用额外元素初始化数组
- 三角化元组
- 在循环中显示不同值的初始化数组
- 如何在macOS中的旧扩展clang和gcc编译器中初始化数组和向量
- 使用每种类型的可变参数模板上的类模板初始化元组
- 跨继承树初始化元组
- 使用对另一个元组不同类型的元素的非常量引用来初始化元组的元素
- 在可变参数模板类的构造函数中初始化元组成员
- 用相同的参数初始化元组