3

After seeing the benefits of std::array I was trying to create a class that supports multiple dimensions.

My initial experiments used nested std::array. I chose not to use this method partly for the ugly way to write the type.

ie: std::array<std::array<std::array<...>, >, >

The new class is mostly working except for initialization. I haven't decided whether its best to use inheritance or containment. The choice may depend on whether I can get initialization working.

How can I get either of the last two lines of this to compile:

// multi-dimensional array based on std::array

#include <array>

template <class T, int s, int... r>
class arraynd_a : public std::array<arraynd_a<T, r...>, s>
{
public:
};

template <class T, int s>
class arraynd_a<T, s> : public std::array<T, s>
{
public:
};

template <class T, int s, int... r>
class arraynd_b
{
public:
    std::array<arraynd_b<T, r...>, s> arr;
};

template <class T, int s>
class arraynd_b<T, s>
{
public:
    std::array<T, s> arr;
};

void test()
{
    constexpr std::array<std::array<int, 2>, 3> a1 = { { { 0, 1 }, { 1, 0 }, { 2, 4 } } };
    /*constexpr*/ arraynd_a<int, 3, 2> a2a;
    /*constexpr*/ arraynd_b<int, 3, 2> a2b;
#if 0
    /*constexpr*/ arraynd_a<int, 3, 2> a3a =  { { { 0, 1 }, { 1, 0 }, { 2, 4 } } };
#endif
#if 0
    /*constexpr*/ arraynd_b<int, 3, 2> a3b =  { { { 0, 1 }, { 1, 0 }, { 2, 4 } } };
#endif
}
3
  • If your worried about the type of nested array looking ugly, consider using a typedef or a using declaration. Commented Sep 13, 2018 at 8:03
  • Accessing array as: a2b.arr = {...} does the job, but i can't figure out how to do this with aggregate initialization Commented Sep 13, 2018 at 8:13
  • I suggest, that you have a look at a library, such as eigen or amadrillo. You don't have to reinvent it. eigen.tuxfamily.org/dox-devel/unsupported/… Commented Sep 13, 2018 at 12:10

2 Answers 2

1

If you are doing the member class way, you have to wrape the array content one more time with {} (you also do not initialize an array with std::array<int, 2> arr = 1, 2;, or?):

template <class T, std::size_t DIM, std::size_t... ARGS>
struct arr_mult_dim
{
    std::array<arr_mult_dim<T, ARGS...>, DIM> arr_;
};
template <class T, int DIM>
struct arr_mult_dim<T, DIM>
{
    std::array<T, DIM> arr_;
};
template <class T, std::size_t... DIMS>
using arr_mult_dim_t = arr_mult_dim<T, DIMS...>;

And use it like that:

arr_mult_dim_t<int, 2> arr_1 = { { 0, 1 } };

But a more pretty way to do it would be if you generate the needed type with a nested using declaration:

template <class T, std::size_t DIM, std::size_t... ARGS>
struct arr_mult_dim
{
    using type = std::array<typename arr_mult_dim<T, ARGS...>::type, DIM>;
};
template <class T, std::size_t DIM>
struct arr_mult_dim<T, DIM>
{
    using type = std::array<T, DIM>;
};
template <class T, std::size_t... DIMS>
using arr_mult_dim_t = typename arr_mult_dim<T, DIMS...>::type;

The usage would be:

arr_mult_dim_t<int, 2> arr_1 = { 0, 1 };
arr_mult_dim_t<int, 2, 2> arr_2 = { { { 0, 1 }, {0, 1} } };
arr_mult_dim_t<int, 2, 2, 2> arr_3 =
{
    {
        {
            {
                {0, 1 },
                { 0, 1 }
            }
        },
        {
            {
                { 0, 1 },
                { 0, 1 }
            }
        }
    }
};

Now you don't need to use the extra {}.

EDIT: I did some research. I don't know why your inheritation solution isn't working. The class which inherites from a class with aggregate propierties should also have aggregate propiertes. The standard says:

The elements of an aggregate are: ...
- for a class, the direct base classes in declaration order followed by the direct non-static data members
in declaration order.

Looks like this isn't implemented yet. In older standards there is a clausel which is explicitly prohibiting aggregate classes to have base classes: Why can I not brace initialize a struct derived from another struct?

Sign up to request clarification or add additional context in comments.

4 Comments

std::array's size parameter is a std::size_t not an int, consider using that instead
Thanks, So in the form of my original question the answer is more braces: arraynd_b<int, 3, 2> a3b = { { { { { { 0, 1 } } }, { { { 1, 0 } } }, { { { 2, 4 } } } } } };
So, the type of arr_mult_dim_t<int, 2, 2, 2> arr_3 actually is std::array<std::array<std::array<std::array<int>, 2>, 2>, 2>, its just defined using the nested using declaration. This does look better regarding the braces.
@RadAd yes, exactly
0

I was able to keep my original design and initialize it from a native multidimensional array:

#include <array>

template <class T, size_t s, size_t...r>
struct arraynd
{
    typedef arraynd<T, r...> inner_type;
    typedef typename inner_type::native_type native_type[s];

    static constexpr std::array<inner_type, s> to_arraynd(const native_type& init)
    {
        return impl_to_arraynd(init, std::make_index_sequence<s> {});
    }

    template <std::size_t... I>
    static constexpr std::array<inner_type, s> impl_to_arraynd(const native_type& init, std::index_sequence<I...>)
    {
        return { inner_type(init[I])... };
    }

    constexpr arraynd()
    {
    }

    constexpr arraynd(const native_type& init)
        : inner(to_arraynd(init))
    {

    }

    std::array<inner_type, s>   inner;
};

template <class T, size_t s>
struct arraynd<T, s>
{
    typedef T inner_type;
    typedef T native_type[s];

    static constexpr std::array<inner_type, s> to_arraynd(const native_type& init)
    {
        return impl_to_arraynd(init, std::make_index_sequence<s> {});
    }

    template <std::size_t... I>
    static constexpr std::array<inner_type, s> impl_to_arraynd(const native_type& init, std::index_sequence<I...>)
    {
        return { inner_type(init[I])... };
    }

    constexpr arraynd()
    {
    }

    constexpr arraynd(const native_type& init)
        : inner(to_arraynd(init))
    {

    }

    std::array<inner_type, s>   inner;
};

int main()
{
    constexpr int a2native[2][3] = { { 1, 2, 3 }, { 1, 2, 3 } };
    constexpr std::array<std::array<int, 3>, 2>   a2std = { { { 1, 2, 3 }, { 1, 2, 3 } } };
    constexpr arraynd<int, 2, 3> a2({ { 1, 2, 3 }, { 1, 2, 3 } });

    return 0;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.