c++ - 创建一个用 0,1,2,3 元组初始化的多维数组,

标签 c++ templates multidimensional-array c++14 variadic-templates

函数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;
}

这可以是 C++17 中的 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 留给读者作为练习。

关于c++ - 创建一个用 0,1,2,3 元组初始化的多维数组,,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36388870/

相关文章:

java - 如何在indexOf中使用int[][]之外的列表?

php - MySQL 中使用 PHP 的多维数组和聚合函数?

c++ - ClCompile 项目也是一项任务吗?

C++: STRING 函数返回十六进制值而不是字符串

c++ - 不带参数的部分特化函数模板

c++ - 使用枚举参数重载模板时出现 MSVC 编译器错误

c++ - 模板模板成员继承 'using'

c++ - 我怎样才能实现一个通用的最小值?

c++ - 试图读取或写入 protected 内存 : C++ Modified Value of Memory

java - 多维数组的新手