48

In C++11 std::array is defined to have contiguous storage and performance that is no worse than an array, but I can't decide if the various requirements of the standard imply that std::array has the same size and memory layout as a normal array. That is can you count on sizeof(std::array<int,N>) == sizeof(int)*N or is that implementation specific?

In particular, is this guaranteed to work the way you would expect it to:

std::vector< std::array<int, N> > x(M);
typedef (*ArrayPointer)[N];
ArrayPointer y = (ArrayPointer) &x[0][0];
// use y like normal multidimensional array

It works in the two compilers I tried (GNU & Intel). Furthermore, all the 3rd party documentation I could find (like this), states that std::array is just as memory efficient as a plain array, which combined with the contiguous requirement would imply that it must have identical memory layout. However I can't find this requirement in the standard.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
pavon
  • 16,574
  • 1
  • 23
  • 25
  • 1
    There is no such guarantee in `23.3.2 Class template array`, and indeed, searching the standard for `sizeof` seems to only turn up the following guarantees for me: `char` and its variants are `1`, and `nullptr` has the same sizeof as `void*`. – us2012 Sep 30 '13 at 20:37
  • As @us2012 states, there is no explicit guarantee. While it may work on your platform of choice, it may fail on other platforms with different alignment constraints. – Nathan Ernst Sep 30 '13 at 20:43
  • I suppose you can simply have a `static_assert(sizeof(std::array) == sizeof(T)*N)` for all types in your code as a tripwire for an unusual library implementation (or some alignment option which causes `std::array` to align differently). If the sizes are equal the layout must be the same. – Peter - Reinstate Monica Dec 28 '20 at 21:59

2 Answers2

34

It's nearly required. Specifically, §23.3.2.1/2 says:

An array is an aggregate (8.5.1) that can be initialized with the syntax

array<T, N> a = { initializer-list };

where initializer-list is a comma-separated list of up to N elements whose types are convertible to T.

Since it's an aggregate, it can't use any sort of constructor to convert the data in the initializer-list to the correct format. That really only leaves one possibility: about the only thing it can store are the values themselves.

I suppose it would be possible for an std::array to store some sort of auxiliary data following the specified data, such as extra memory set to some predefined value, so if you write past the end of the array, you'd probably change that data. The compiler/run-time would then check those values at shut-down, and if you'd changed the values, report your code's undefined behavior.

It's also possible that a compiler could do padding/alignment differently for an std::array than for a built-in array. One obvious example for which this could even be desirable would be to support super-alignment requirements, such as data for use with Intel's SSE instructions. A built-in array can't support super-alignment, but I think the specification of std::array might be barely loose enough to allow it.

Bottom line: without getting into questions of how many possibilities might exist, it's pretty clear that std::array doesn't necessarily have to follow the rule you're asking about.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • 2
    I think it can also have padding, even at the beginning, as it isn't required to be a standard-layout class (and can't be, for its members may be of non-standard-layout type). – dyp Sep 30 '13 at 20:52
  • 1
    [Live example](http://coliru.stacked-crooked.com/a/34cc30147f6c92c4) (Yes, this doesn't prove anything. I carefully searched the Standard and didn't find any *layout* requirements for aggregates or literal types, just for standard-layout types; formerly PODs had these requirements in C++03.) – dyp Sep 30 '13 at 21:01
  • 1
    C forbids structures to have unnamed padding at their beginning in 6.7.2.1/15 "There may be unnamed padding within a structure object, but not at its beginning." (i.e. you could still have it at the end) – dyp Sep 30 '13 at 21:07
  • Yeah -- rereading things, I think you're right -- there could also be unnamed padding at the beginning. – Jerry Coffin Sep 30 '13 at 21:12
  • 1
    You might be interested in [this](http://stackoverflow.com/questions/15512827) as well, especially given that `0` is a special case for `std::array`. – Daniel Frey Sep 30 '13 at 21:27
  • @DyP, from the descriptions of aggregate and standard-layout, the former can be disqualified from the latter only if at least one member's type is not standard-layout. So an implementation would be really obnoxious if it adds an unlisted member that's never standard-layout to `std::array`. – CTMacUser Oct 01 '13 at 06:25
  • Types that qualify as standard-layout do not have beginning padding. The `complex` (for `float`, `double`, `long double`, and compiler-specified other legal arguments) and `aligned_storage` class templates are required to have no padding. Besides those cases, there is no Standard way, AFAIK, to prevent a class type from having beginning, intermediate, and/or ending padding. – CTMacUser Oct 01 '13 at 06:30
  • The standard (probably an incarnation past 2013) appears to say (quoted from [here](https://stackoverflow.com/a/25173547/3150802)) abut std::array when it explains "contiguous": "it obeys the identity &a[n] == &a[0] + n for all 0 <= n < N". This would imply no intermediate padding. – Peter - Reinstate Monica Jul 10 '19 at 10:38
  • 1
    "I suppose it would be possible for an std::array to store some sort of auxiliary data following the specified data, such as extra memory set to some predefined value, so if you write past the end of the array, you'd probably change that data." the wording of https://en.cppreference.com/w/cpp/container/array appears to suggest that this is not possible: "This container is an aggregate type with the same semantics as a struct holding a C-style array T[N] as its only non-static data member." Not sure which is correct. – Ciro Santilli OurBigBook.com Apr 23 '20 at 10:54
  • 1
    @CiroSantilliOurBigBook.com I don't see any statement like that in the actual standard (I would expect it to be somewhere in https://eel.is/c++draft/array) – Ben Voigt Jan 18 '23 at 21:14
0

I am not sure what the standard guarantee is exactly but by using simple logic I think that it is a pretty safe assumption that sizeof(std::array<T,N>) == sizeof(T[N]).

First of all the design goal of std::array is to work around the problem of array type decay when passed or returned to/from functions.

A secondary goal of std::array is to provide a minimalist STL container interface by implementing some regular methods like size(), begin(), end(), and so on.

The first goal is achieved by creating a new class/type with which the array dimension is encoded in the type, therefore array decay is not possible.

That is the only purpose of std::array. Now, if you think about how this is implemented. It should look something like this:

template <class T, size_t N>
array
{
private:
  T a_[N];
};

I have a very hard time thinking why an implementation would generate a class that is of a different size!

Even if sizeof(T) is not a perfect multiple of the enforced alignment. The equality should still hold because the padding is only added between variables in a structure. As far as I know, padding is not added at the end of class to round its size.

lano1106
  • 158
  • 1
  • 8