2

Here's a simple 2-dimensional C-style array example:

int c[2][2] = {
    { 1, 2 },
    { 3, 4 }
};

If I want this to be std::array, I have to use this:

std::array<std::array<int, 2>, 2> c = {
    { { 1, 2 } },
    { { 3, 4 } }
};

The declaration is more involved, and I have to use extra { } for initialization.

Is it possible to create a C-style array wrapper, with which I can do this?

my_array<int, 2, 2> c = {
    { 1, 2 },
    { 3, 4 }
};

So the declaration is simpler, and there is no need of the extra { }.

If creating something like this is possible, would this solution have some drawbacks compared to std::array?


I've managed to do this:

template <typename T, std::size_t ...S>
struct ArrayTypeHelper;

template <typename T, std::size_t HEAD, std::size_t ...TAIL>
struct ArrayTypeHelper<T, HEAD, TAIL...> {
    using Type = typename ArrayTypeHelper<T, TAIL...>::Type[HEAD];
};
template <typename T, std::size_t HEAD>
struct ArrayTypeHelper<T, HEAD> {
    using Type = T[HEAD];
};

template <typename T, std::size_t ...S>
struct my_array {
    typename ArrayTypeHelper<T, S...>::Type data;
};

But this still needs extra { }:

my_array<int, 2, 2> b = { {
    { 1, 2 },
    { 3, 4 }
} };
12
  • See also stackoverflow.com/questions/34832090/… Commented Dec 2, 2018 at 16:20
  • How about std::array<std::array<int, 2>, 2> c{1, 2, 3, 4};? Commented Dec 2, 2018 at 16:22
  • With C++17 you can do it simpler since it can deduce the template arguments from the initialization. Commented Dec 2, 2018 at 16:27
  • @VTT: I don't really like this style. And there are compilers which warn for this. Commented Dec 2, 2018 at 16:28
  • 2
    @geza I guess by "some compilers" you mean clang? Then it is a known bug. You can still keep each row on separate line to make it look like matrix Commented Dec 2, 2018 at 16:33

1 Answer 1

3

The problem is that a c-array member is always viewed by the brace elision algorithm as a single entity. If the inner initializer begins with a brace , the algorithm expects an initializer list with one element. So the solution is to define the carray wrapper in such a way that the brace elision algorithm knows how many elements are aggregated by the wrapper.

To do this, the only solution I see is to simulate an array through multiple inheritance:

#include <utility>
using namespace std;

template<class T,size_t DeDuplicate>
struct holder{
  T val;
  };

template<class T,class IndSeq>
struct carray_wrapper_b;

template<class T,size_t...Is>
struct carray_wrapper_b<T,index_sequence<Is...>>
  :holder<T,Is>...
  {   };

template<class T,size_t I,size_t...Is>
struct make_carray_{
  using type = carray_wrapper_b<typename make_carray_<T,Is...>::type
                               ,make_index_sequence<I>>;
  };
template<class T,size_t I>
struct make_carray_<T,I>{
  using type = carray_wrapper_b<T,make_index_sequence<I>>;
  };

template<class T,size_t...Is>
using carray_wrapper = typename make_carray_<T,Is...>::type;

carray_wrapper<int,2,2>  x = { {1,2},{3,4}};
carray_wrapper<int,2,2>  y = { 1,2,3,4};

This array wrapper can also be used for initialization purpose only:

template<class T,size_t I,size_t...Is>

struct carray{
  carray(initializer_list<T>);
  carray(initializer_list<carray_wrapper<T,Is...>>);
  };

carray<int,2,2,3> arr{1,2,3,4,5,6,7};
carray<int,2,2,3> arr2{{1,2},{3,4}};
carray<int,2,2,3> arr3{{{1,2},{3,4}},{1,2}};
Sign up to request clarification or add additional context in comments.

3 Comments

@geza Finaly I found a solution, you can make a c-style array member of your wrapper class and use initializer_list constructors that takes as elements the carray_wrapper I proposed.
Nice trick, thanks Oliv! I'd upvote this answer more if I could, it deserves more than 2.
@geza Thank you :)

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.