用模板初始化的N维std :: array

时间:2014-10-31 20:46:41

标签: c++ arrays c++11

请考虑以下代码:

#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] =真,而[0] [0] [2] [1] =真,并且每隔一个元素错误。但我遇到了多个模板包的问题,​​似乎无法使其正常运行。任何帮助,将不胜感激。感谢。

4 个答案:

答案 0 :(得分:5)

那么这是一个有效的解决方案。如果有人可以改进它,我会非常有兴趣看到它,因为我不知道其他任何方法。

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

答案 1 :(得分:2)

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

#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

DEMO (C++14)

DEMO 2 (C++11)

答案 2 :(得分:1)

解决方案包含参数包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
}

答案 3 :(得分:0)

这是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')

其中I<3,6,5,4>设置多数组的维度。然后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
}