【问题标题】:Creating a multi-dimensional array initialized with tuples of 0,1,2,3,创建一个用 0,1,2,3 元组初始化的多维数组,
【发布时间】:2016-07-23 04:20:45
【问题描述】:

函数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&lt;M,N&gt;() 扩展到createMultiArray&lt;Dimensions...&gt;() 到任意数量的维度,例如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&lt;Dimensions...&gt;()的返回类型就是

    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...);
}

【问题讨论】:

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


    【解决方案1】:

    我们不要让事情变得不必要的复杂。从概念上讲,您要编写的初始化只是一堆嵌套的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 留给读者作为练习。

    【讨论】:

      【解决方案2】:

      这里我相信 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 返回类型)。

      【讨论】:

        【解决方案3】:

        好的,我想我有一个可行的解决方案。明天我会努力改进它。使用std::index_sequence 实际上给我带来了烦人的错误,因为它只是std::integer_sequence&lt;std::size_t, Is...&gt; 的别名,它不适用于我的ExpandPacks 类(我稍后会尝试将它们放回),所以我定义了sequence 和@ 987654325@ 来自第一原则并使用它们。

        #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)
        

        不过,可能有一个更短的解决方案。我会尝试改进这个丑陋的解决方案,但至少我让它工作了。欢迎更好的解决方案。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-06-26
          • 2014-12-09
          • 2015-08-01
          • 1970-01-01
          相关资源
          最近更新 更多