The simplest way to do this is by nesting std::array:
#include<array>
template<class T, size_t size, size_t... sizes>
struct ArrayImpl {
using type = std::array<typename ArrayImpl<T, sizes...>::type, size>;
};
template<class T, size_t size>
struct ArrayImpl<T, size> {
using type = std::array<T, size>;
};
template<class T, size_t... sizes>
using Array = typename ArrayImpl<T, sizes...>::type;
In this solution Array<char, 3, 4> is the same as std::array<std::array<char, 4>, 3> - array consisting of arrays of smaller dimension.
This also shows how you can implement operator[] for many dimensions. operator[] of your object needs to return object for which operator[] is also defined. In this case it is reference to an array of smaller dimension.