2014-10-31 72 views
2

初始化N维的std ::阵列考虑下面的代码:使用模板

#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>(); 
} 

我想是能够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 ] = true,其他每个元素都是false。但我有多个模板包的问题,​​似乎无法让它工作。任何帮助,将不胜感激。谢谢。

+0

我认为我有怎样的一个的[相关问题一次(http://stackoverflow.com/questions/18251815/creating-an-array-initializer-from-a-tuple-or-variadic-template α参数)。请不要在意问题太多,请检查有用的答案。 – 2014-10-31 20:51:10

+1

什么是你的算法是什么意思?你怎么知道它是一个有2个元素的4维阵列与5个元素的2维阵列? – Barry 2014-10-31 20:52:38

+1

你不能有这样的多个非推导参数包,编译器无法知道哪里结束,另一个开始 – Brian 2014-10-31 20:53:13

回答

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 
} 
+0

+1干得好。我会添加一个static_assert来检查指数的数量是否好于'NDimensionalArray'中的第一件事。 – jrok 2014-10-31 21:32:15

+0

为我学习的东西! – prestokeys 2014-10-31 21:33:11

+0

用'narray.fill(T {})替换'narray.fill(false);'似乎不能编译。但memset(&narray,T {},sizeof(narray));'确实有效。 – prestokeys 2014-10-31 21:52:41

2

你想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 

DEMO (C++14)

DEMO 2 (C++11)

+0

只是好奇。发布了3个有趣和不同的解决方案,每个解决方案都表示他们的解决方案很难看。这是否意味着问题的复杂性不可避免地导致了一个丑陋的解决方案,或者他们的解决方案只是他们怀疑可以而且应该改进的“初稿”? – prestokeys 2014-11-01 02:30:21

+0

@prestokeys其实当我现在看它时,它不是*那么糟糕,但仍然难以阅读。我添加了C++ 11版本,我不认为这种特定的方法可以更简化。 * ugliness *是将包拆分成子序列以及使用该子序列访问数组元素的结果。它也产生了很多每个函数的实例。但至少你有你想要的语法,没有'seq <>'包装器。 – 2014-11-01 10:00:01

+0

@prestokeys是啊,既不是我读过的最糟糕的代码,但它基本上是一个复杂的问题,你问的权利......把一堆东西扔在模板上,让它做不同的事情?如果解释这个问题太复杂了,那么答案可能就会太大。 – Barry 2014-11-01 16:08:00

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

这里是彼得的解决办法收拾了一下,除去他的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 
}