7

Can I use something like std::array<int[2][2], 2> as a substitute for int[2][2][2], just like std::array<int, 2> can be used instead of int[2]?

What I really need is maybe a statically-sized multidimensional array that

  1. Have "proper" value semantics, and
  2. Is stored contiguously in memory.

It seems that, unlike C-style arrays, std::array of std::array is not guaranteed to have fully compactified memory, as std::array may contain padding.

What are possible issues I might have if I use something like std::array<int[2][2], 2>? Perhaps this is a too vague question, but it's hard to figure out why exactly I'm not comfortable and somewhat doubtful using it for my purpose.

Junekey Jeon
  • 1,496
  • 1
  • 11
  • 18
  • 4
    The trick is to abstract away the dimensions. Use a `std::array` and wrap in a class, overload the `()` operator to provide element access as if it is 3d instead of flat. – NathanOliver Mar 03 '18 at 14:06
  • i dont know what to tell you. if you are not comfortable with it then dont use it. create your own array class or use cstyle array. – JoshKisb Mar 03 '18 at 14:08
  • 1
    Why do you need to store these elements contiguously? – xskxzr Mar 03 '18 at 14:09
  • 1
    This question is hard to answer, because I can't come up with any potential "issue" of this use, though this array looks not very natural. – llllllllll Mar 03 '18 at 14:11
  • @liliscent That's exactly what I feel right now. – Junekey Jeon Mar 03 '18 at 14:13
  • @xskxzr Because I need to use a single pointer to iterate over the array. With padding, things will become complicated. – Junekey Jeon Mar 03 '18 at 14:17
  • 1
    Another thing you could do is assert the size is correct. Something like `static_assert(sizeof(std::array, y>, z>) == sizeof(int) * x * y * z, "Array has padding");` If that doesn't fire you know you are safe – NathanOliver Mar 03 '18 at 14:19
  • The iteration possibly [results in undefined behavior](https://stackoverflow.com/a/6016700/5376789). You'd better think of other ways. – xskxzr Mar 03 '18 at 14:24
  • @xskxzr Interesting. I've never know that. Thank you. Now it seems that I should do what NathanOliver suggested, though making a fully generic & compatible & not-bloated multidimensional array class seems to be very non-trivial... – Junekey Jeon Mar 03 '18 at 14:47

1 Answers1

3

No, it results in undefined behavior.

The value_type of a container must be Erasable from the container type, where Erasable is defined in [container.requirements.general] paragraph 15:

Given an allocator type A and given a container type X having a value_­type identical to T and an allocator_­type identical to allocator_­traits<A>​::​rebind_­alloc<T> and given an lvalue m of type A, a pointer p of type T*, an expression v of type (possibly const) T, and an rvalue rv of type T, the following terms are defined. If X is not allocator-aware, the terms below are defined as if A were allocator<T> — no allocator object needs to be created and user specializations of allocator<T> are not instantiated:

  • ...

  • T is Erasable from X means that the following expression is well-formed: allocator_traits<A>::destroy(m, p)

Because std::array is not allocator-aware, we need to check if allocator_traits<allocator<int[2][2]>>::destroy(m, p) is welll formed.

Since std::allocator has no member function destroy (deprecated in C++17), std::allocator_traits::destroy would call the (pseudo) destrutor of int[2][2] directly. This is ill-formed because int[2][2] is not a scalar type.

Community
  • 1
  • 1
xskxzr
  • 12,442
  • 12
  • 37
  • 77
  • 6
    This seems to me to be a defect. You can't erase members of a `std::array` as it is an aggregate. It doesn't use an allocator at all. – NathanOliver Mar 03 '18 at 15:52
  • It is explicitly mentioned in the standard that ````std::array```` should satisfy all the requirements of Container (except some things irrelevant here), so technically this answer seems to be absolutely correct. Thank you! – Junekey Jeon Mar 04 '18 at 15:20