Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Variadic template for multidimensional std::array

We can alias a multidimensional array like this:

template<typename T, size_t size1, size_t size2>
using myArray = std::array<std::array<T, size2>, size1>;

But this only allows us a predefined number of dimensions. Is there a way to turn this into a variadic template, so that we could write any of those:

myArray<int, 2, 2, 2> arr3d;
myArray<int, 2, 2, 2, 2> arr4d;

I've tried a few things but wasn't completely satisfied with any of them.

This:

template<typename T, size_t size>
using myArray<T, size> = std::array<T, size>;

template<typename T, size_t size, size_t... more>
using myArray = std::array<myArray<T, more...>, size>;

doesn't even compile because template specializations are apparently not allowed with alias templates.

This is currently my best solution but deletes all constructors of std::array which I would like to keep:

template<typename T, size_t size, size_t... more>
struct myArray : public std::array<myArray<T, more...>, size> {};

template<typename T, size_t size>
struct myArray<T, size> : public std::array<T, size>{};

And with this solution I would always have to write ".internal" in front of every array access:

template<typename T, size_t size, size_t... more>
struct myArr {
    std::array<myArr<T, more...>, size> internal;
};

template<typename T, size_t size>
struct myArr<T, size> {
    std::array<T, size> internal;
};

So could anyone think of a solution like the second but where I could keep the constructors? I can't.

like image 569
Michael Mahn Avatar asked Mar 05 '23 06:03

Michael Mahn


2 Answers

I would suggest simply multiplying the dimensions together and having a single std::array. E.g.: std::array<int, D0 * D1 * D2 * D3>. You can then provide utility functions or a wrapper class to convert a multi-dimensional index into one-dimensional.

Anyway...

Here's a simple recursive solution that uses explicit template specialization:

template <typename T, std::size_t... Ds>
struct nested_array;

template <typename T, std::size_t D>
struct nested_array<T, D>
{
    using type = std::array<T, D>;
};

template <typename T, std::size_t D, std::size_t... Ds>
struct nested_array<T, D, Ds...>
{
    using type = std::array<typename nested_array<T, Ds...>::type, D>;
};

static_assert(std::is_same_v<
    typename nested_array<int, 1, 2, 3>::type,
    std::array<std::array<std::array<int, 3>, 2>, 1>
>);

live example on wandbox


Here's a solution based on variable template partial specialization:

template <typename T>
struct t { using type = T; };

template <typename T>
using unwrap = typename T::type;

template <typename T, std::size_t... Ds>
constexpr auto nested_array = t<void>{};

template <typename T, std::size_t D>
constexpr auto nested_array<T, D> = t<std::array<T, D>>{};

template <typename T, std::size_t D, std::size_t... Ds>
constexpr auto nested_array<T, D, Ds...> = 
    t<std::array<unwrap<decltype(nested_array<T, Ds...>)>, D>>{};

static_assert(std::is_same_v<
    unwrap<decltype(nested_array<int, 1, 2, 3>)>,
    std::array<std::array<std::array<int, 3>, 2>, 1>
>);

live example on wandbox

like image 142
Vittorio Romeo Avatar answered Mar 13 '23 00:03

Vittorio Romeo


You might use extra layer:

template<typename T, size_t size, size_t... more>
struct myArray_impl
{
    using type = std::array<typename myArray_impl<T, more...>::type, size>;
};

template<typename T, size_t size>
struct myArray_impl<T, size>
{
    using type = std::array<T, size>;
};

template<typename T, size_t size, size_t... more>
using myArray = typename myArray_impl<T, size, more...>::type;
like image 39
Jarod42 Avatar answered Mar 13 '23 00:03

Jarod42