使用模板初始化的N维std::数组

N-dimensional std::array initialized with templates

本文关键字:std 数组 初始化      更新时间:2023-10-16

考虑以下代码:

#include <iostream>
#include <array>
template <typename, int, int...> struct NArray;
template <typename T, int NUM_DIMENSIONS, int N>
struct NArray<T, NUM_DIMENSIONS, N> {
    using type = std::array<T, N>;
};
template <typename T, int NUM_DIMENSIONS, int FIRST, int... REST>
struct NArray<T, NUM_DIMENSIONS, FIRST, REST...> {
    using type = std::array<typename NArray<T, NUM_DIMENSIONS, REST...>::type, FIRST>;
};
template <typename T, int NUM_DIMENSIONS, int... N>
typename NArray<T, NUM_DIMENSIONS, N...>::type NDimensionalArray() {
    typename NArray<T, NUM_DIMENSIONS, N...>::type nArray;
    return nArray;
}
int main() {
    const auto nArray = NDimensionalArray<int,4, 2,4,5,3>();
}

我想要的是能够用更多的int值扩展NDimensionalArray的模板包,以便将某些值初始化为某个指定的固定值。例如,

auto a = NDimensionalArray<bool,4, 2,4,5,3, 1,2,3,2, 0,0,2,1>(true);

将返回一个2x4x5x3 4维std::数组,其中[1][2][3][2]=true,[0][0][2][1]=true,其他每个元素都为false。但我遇到了多个模板包的问题,似乎无法使其正常工作。如有任何帮助,我们将不胜感激。谢谢

这里有一个有效的解决方案。如果有人能改进它,我会很有兴趣看到它,因为我不知道其他方法

#include <iostream>
#include <array>
#include <cstring>
template <int... > struct seq {};
template <typename, int...> struct NArray;
template <typename T, int N>
struct NArray<T, N> {
    using type = std::array<T, N>;
};
template <typename T, int FIRST, int... REST>
struct NArray<T, FIRST, REST...> {
    using type = std::array<typename NArray<T, REST...>::type, FIRST>;
};
template <typename T, typename Dim>
struct make_narray;
template <typename T, int... N>
struct make_narray<T, seq<N...>>
{
    using type = typename NArray<T, N...>::type;
};
template <typename T>
T& get(T& val, seq<>)
{
    return val;
}
template <typename NA, int E0, int... Es>
auto get(NA& arr, seq<E0, Es...>)
-> decltype(get(arr[E0], seq<Es...>{}))
{
    return get(arr[E0], seq<Es...>{});
}
template <typename T, typename Dim, typename... Elems>
typename make_narray<T, Dim>::type
NDimensionalArray(T val)
{
    typename make_narray<T, Dim>::type narray{};
    auto _{get(narray, Elems{}) = val ...};  // Quick initialization step!
    return narray;
}
int main() {
    auto a = NDimensionalArray<bool, seq<2, 4, 5, 3>, seq<1, 2, 3, 2>, seq<0, 0, 2, 1>>(true);
    std::cout << std::boolalpha;
    std::cout << a[0][0][0][0] << std::endl; // prints false
    std::cout << a[1][2][3][2] << std::endl; // prints true
    std::cout << a[0][0][2][1] << std::endl; // prints true
}

您想要的NDimensionalArray<bool,4, 2,4,5,3, 1,2,3,2, 0,0,2,1>(true)的确切语法,在C++14和C++11中(第二个演示):

#include <iostream>
#include <iomanip>
#include <array>
#include <tuple>
#include <utility>
#include <type_traits>
#include <cstddef>
template <typename, int, int...> struct NArray;
template <typename T, int NUM_DIMENSIONS, int N>
struct NArray<T, NUM_DIMENSIONS, N>
{
    using type = std::array<T, N>;
};
template <typename T, int NUM_DIMENSIONS, int FIRST, int... REST>
struct NArray<T, NUM_DIMENSIONS, FIRST, REST...>
{
    using type = std::array<typename NArray<T, NUM_DIMENSIONS, REST...>::type, FIRST>;
};
template <typename A, typename T>
void assign(A& arr, const T& value)
{
    arr = value;
}
template <int I, int... Is, typename A, typename T>
void assign(A& arr, const T& value)
{
    assign<Is...>(arr[I], value);
}
template <int SIZE, int PACK, int... Ind, typename T, typename A, std::size_t... Is>
auto set(const T& value, A& arr, std::index_sequence<Is...> seq)
    -> std::enable_if_t<(SIZE*PACK == sizeof...(Ind))>
{    
}
template <int SIZE, int PACK, int... Ind, typename T, typename A, std::size_t... Is>
auto set(const T& value, A& arr, std::index_sequence<Is...> seq)
    -> std::enable_if_t<(SIZE*PACK < sizeof...(Ind))>
{    
    constexpr auto t = std::make_tuple(Ind...);
    assign<std::get<PACK*SIZE+Is>(t)...>(arr, value);
    set<SIZE, PACK+1, Ind...>(value, arr, seq);
}
template <typename T, int DIMS, int... N, std::size_t... Is>
auto make_narray(const T& value, std::index_sequence<Is...> seq)
{
    constexpr auto t = std::make_tuple(N...);
    typename NArray<T, DIMS, std::get<Is>(t)...>::type arr{};
    set<DIMS, 1, N...>(value, arr, seq);
    return arr;
}
template <typename T, int DIMS, int... N>
auto NDimensionalArray(const T& value)
{    
    return make_narray<T, DIMS, N...>(value, std::make_index_sequence<DIMS>{});
}
int main()
{
    auto a = NDimensionalArray<bool,4, 2,4,5,3, 1,2,3,2, 0,0,2,1>(true);
    std::cout << std::boolalpha;
    std::cout << a[1][2][3][2] << std::endl; // ~~~~^
    std::cout << a[0][0][2][1] << std::endl; // ~~~~~~~~~~~~^
    std::cout << a[0][0][0][0] << std::endl; // (not set)
}

输出:

true
true
false

演示(C++14)

演示2(C++11)

在参数包ARGS&&... args中具有初始化位置的解决方案:

#include <array>
#include <iostream>
#include <deque>
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 <typename E, typename Container, typename T>
void assign (E& element, Container&&, const T& v) { element = v; }
template <typename Subarray, std::size_t N, typename Container, typename T>
void assign (std::array<Subarray, N>& narray, Container&& pos, const T& v) {
    const std::size_t index = pos.front();
    pos.pop_front();
    assign (narray[index], pos, v);
}
template <typename T, int... Dimensions, typename... Args>
typename NArray<T, Dimensions...>::type NDimensionalArray (const T& value, Args&&... args) {
    typename NArray<T, Dimensions...>::type narray{};
    const auto initializer = {std::forward<Args>(args)...};
    const int groupSize = sizeof...(Dimensions),  numGroups = initializer.size() / groupSize;
    for (std::size_t i = 0;  i < numGroups;  i++)
        assign (narray, std::deque<std::size_t>(initializer.begin() + i*groupSize, initializer.begin() + (i+1)*groupSize), value);
    return narray;
}
int main() {
    const auto multiArray = NDimensionalArray<double, 5,6,7,8,9> (3.14,  1,2,3,2,4,  3,3,2,1,2,  0,1,3,1,2);
    std::cout << multiArray[1][2][3][2][4] << 'n';  // 3.14
    std::cout << multiArray[3][3][2][1][2] << 'n';  // 3.14
    std::cout << multiArray[0][1][3][1][2] << 'n';  // 3.14
}

以下是Piotr的解决方案,通过删除他的enable_if专门化并再次使用索引技巧,对其进行了一些整理。此外,我还将任意数量的集值概括为以下语法示例:

makeNDimensionalArray<char, I<3,6,5,4>, I<2,4,3,2, 0,1,2,3, 1,2,4,3>, I<0,0,0,0, 2,3,1,2>, I<1,1,2,1>>('a','b','c')

其中CCD_ 3设置多阵列的维度。然后I<2,4,3,2, 0,1,2,3, 1,2,4,3>将数组的这三个索引位置设置为"a",I<0,0,0,0, 2,3,1,2>将数组的那两个索引位置设为"b",依此类推。

#include <iostream>
#include <array>
#include <tuple>
#include <utility>
template <typename, std::size_t, std::size_t...> struct NArray;
template <typename T, std::size_t NumDimensions, std::size_t N>
struct NArray<T, NumDimensions, N> {
    using type = std::array<T, N>;
};
template <typename T, std::size_t NumDimensions, std::size_t First, std::size_t... Rest>
struct NArray<T, NumDimensions, First, Rest...> {
    using type = std::array<typename NArray<T, NumDimensions, Rest...>::type, First>;
};
template <typename T, std::size_t... Dimensions>
using NDimensionalArray = typename NArray<T, sizeof...(Dimensions), Dimensions...>::type;
template <typename T, typename Dimensions> struct NArrayFromPack;
template <typename T, template <std::size_t...> class P, std::size_t... Dimensions>
struct NArrayFromPack<T, P<Dimensions...>> : NArray<T, sizeof...(Dimensions), Dimensions...> {
    static constexpr std::size_t num_dimensions = sizeof...(Dimensions);
};
template <typename A, typename T>
void setArrayValue (A& a, const T& t) { a = t; }
template <std::size_t First, std::size_t... Rest, typename Array, typename T>
void setArrayValue (Array& array, const T& t) {
    setArrayValue<Rest...>(array[First], t);
}
template <typename Indices, typename Sequence> struct InitializeArray;
template <template <std::size_t...> class P, std::size_t... Is, std::size_t... Js>
struct InitializeArray<P<Is...>, std::index_sequence<Js...>> {
    template <typename Array, typename T>
    static void execute (Array& array, const T& t) {
        constexpr std::size_t GroupSize = sizeof...(Js),  NumGroups = sizeof...(Is) / GroupSize;
        set<GroupSize>(array, t, std::make_index_sequence<NumGroups>{});
    }
private:
    template <std::size_t GroupSize, typename Array, typename T, std::size_t... Ks>
    static void set (Array& array, const T& t, std::index_sequence<Ks...>) {
        const int dummy[] = {(do_set<Ks, GroupSize>(array, t), 0)...};
        static_cast<void>(dummy);
    }
    template <std::size_t N, std::size_t GroupSize, typename Array, typename T>
    static void do_set (Array& array, const T& t) {
        constexpr std::size_t a[] = {Is...};
        setArrayValue<a[N*GroupSize + Js]...>(array, t);
    }
};
template <typename T, typename Dimensions, typename... Indices, typename... Args>
auto makeNDimensionalArray (const Args&... args) {
    using A = NArrayFromPack<T, Dimensions>;
    typename A::type array;
    const int a[] = {(InitializeArray<Indices, std::make_index_sequence<A::num_dimensions>>::execute(array, args), 0)...};
    static_cast<void>(a);
    return array;
}
template <std::size_t...> struct I;
int main() {
    const NDimensionalArray<char, 3,6,5,4> a = makeNDimensionalArray<char, I<3,6,5,4>, I<2,4,3,2, 0,1,2,3, 1,2,4,3>, I<0,0,0,0, 2,3,1,2>, I<1,1,2,1>>('a','b','c');
    std::cout << a[2][4][3][2] << std::endl;  // a
    std::cout << a[0][1][2][3] << std::endl;  // a
    std::cout << a[1][2][4][3] << std::endl;  // a
    std::cout << a[0][0][0][0] << std::endl;  // b
    std::cout << a[2][3][1][2] << std::endl;  // b
    std::cout << a[1][1][2][1] << std::endl;  // c
}