11

std::array is ... (quoting from cppreference):

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.

Does that imply that the address of an array is always the same as the address of its first element, i.e. data()?

#include <array>
#include <iostream>

int main()
{
    std::array<int,6> x{};
    std::cout << &x << "\n";
    std::cout << x.data();
}

Possible output:

0x7ffc86a62860
0x7ffc86a62860

And if yes, is this of any use? Is the following allowed?

int* p = reinterpret_cast<int*>(&x);
for (int i=0;i<6;++i){ std::cout << p[i]; }
Jeff Schaller
  • 2,352
  • 5
  • 23
  • 38
463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
  • 1
    why would you want to do `reinterpret_cast(&x)` rather than just `x.data()`? – Alan Birtles Oct 08 '21 at 19:14
  • C has a rule that the address of any `struct` is equal to the address of its first member, and that a pointer to one can be safely cast to the other. Does C++ have the same rule for `class`es? I am not quite sure where to look in the standard. – Nate Eldredge Oct 08 '21 at 19:17
  • Actually I'm not seeing "only non-static data member" in the standard; the standard doesn't seem to rule out other members. Did cppreference make it up? – Nate Eldredge Oct 08 '21 at 19:21
  • @NateEldredge static members, whether data or methods or whatever, don't take up any space in an object's allocated storage. Only non-static data members do. – Remy Lebeau Oct 08 '21 at 19:22
  • You're making an assumption about the internal layout of a class defined by the standard. As is usual for these things, the standard does not explicitly state what that internal layout should be (nor can I see any mention that it only has one data member). – 1201ProgramAlarm Oct 08 '21 at 19:22
  • @AlanBirtles last part isnt well phrased, I am not actually asking if this is of use, but it is just curiosity – 463035818_is_not_an_ai Oct 08 '21 at 19:23
  • @1201ProgramAlarm I admit I took cppref for granted. If you can quote the standard and it doesnt say that the array is the only data member of an `array`, that would already disproof my assumptions – 463035818_is_not_an_ai Oct 08 '21 at 19:24
  • @RemyLebeau: Right, I know that. What I'm saying is that the Standard doesn't seem to forbid `std::array` from having additional non-static data members, so I am unclear why cppreference asserts that the `T[N]` array is the only one. – Nate Eldredge Oct 08 '21 at 19:24
  • @463035818_is_not_a_number Quote something that isn't there? I dunno, but you can look at one of the [online](https://eel.is/c++draft/array) copies of the standard. – 1201ProgramAlarm Oct 08 '21 at 19:27
  • _Is the following allowed?_ Not necessarily. If `std::array` contains only a member of type `T[N]`, which is, I think, a typical implementation, your code has undefined behavior. – Language Lawyer Oct 09 '21 at 04:14
  • This may be false when `N` is zero, i.e. `std::array` ([example](https://ideone.com/auLya2)) – Sergey Kalinichenko Apr 26 '23 at 12:45

1 Answers1

16

Technically, a std::array object may have padding at the beginning, in which case the data will be at a higher address than the std::array object. Only for standard-layout classes is there a guarantee that the object itself has the same address as the first non-static data member.

There is no guarantee in the standard that std::array<T, N> is standard-layout, even if T is int or something like that. All reasonable implementations of std::array<T, N> will have a single non-static data member of type T[N], no virtual functions, and at most a single inheritance chain with no virtual base classes, which would mean they would be standard-layout as long as T itself is standard-layout. Plus, even if T is not standard-layout, the compiler is not likely to insert padding at the beginning of the std::array object.

So, while an assumption that a std::array<T, N> object has the same address as the first T object it contains is not portable, it is basically guaranteed in practice. And you can add a static_assert(sizeof(std::array<T, N>) == sizeof(T[N])); just to be sure that, if someone ever tries to build your code on an exotic implementation where it isn't the case, they'll know it's not supported.

Brian Bi
  • 111,498
  • 10
  • 176
  • 312
  • The standard does say `std::array` is an aggregate, though. So, doesn't that imply standard-layout? – Remy Lebeau Oct 08 '21 at 19:25
  • 2
    @RemyLebeau aggregate and standard layout are different things. You can have a non-standard layout aggregate. – NathanOliver Oct 08 '21 at 19:25
  • @RemyLebeau I think almost, but not quite. An aggregate class could have multiple (public) base class subobjects of the same type. A standard-layout class can't. Of course, no reasonable implementation would do something like this in the case of `std::array`. – Brian Bi Oct 08 '21 at 19:27
  • @RemyLebeau: I think it's an aggregate in the sense that it is a class with no user-declared or inherited constructors, no private or protected direct non-static data members, etc. But that says nothing about what its members actually are. I think the standard would allow `class std::array { public: int miscellaneous_junk; T actual_array[N]; };` – Nate Eldredge Oct 08 '21 at 19:29
  • 4
    @NateEldredge That particular implementation wouldn't be conforming. The miscellaneous junk members would interfere with aggregate initialization. – Brian Bi Oct 08 '21 at 19:30
  • 1
    _That particular implementation wouldn't be conforming_ You forgot to add «if the impl. doesn't have a special initialization rule for `std::array`». [An `array` is an aggregate that can be list-initialized with up to `N` elements whose types are convertible to `T`.](https://timsong-cpp.github.io/cppwp/n4868/array#overview-2) As long as the impl. supports this, the content of `std::array` can haz any junk in the beginning. Like `std::complex` array reinterpretation as two times longer array of `float`/`double` is a «violation» of [expr.add]/4, an impl. can «violate» [dcl.init.aggr] for `array` – Language Lawyer Oct 09 '21 at 04:01