31

Is there any safe and standard compliant way to treat a C style array as an std::array without copying the data into a new std::array?

This clearly doesn't compile, but is the effect I would like (my real use is more complicated but this short sample should show what I'd like to do). I guess a reinterpret_cast would "work" but probably isn't safe?

#include <array>

int main()
{
    int data[] = {1, 2, 3, 4, 5};

    // This next line is the important one, treating an existing array as a std::array
    std::array<int, 5>& a = data;
}

It feels like it ought to be possible as the data should be stored identically.

edit: To be clear I don't want to clear a new std::array, I want to refer to the existing data as one.

jcoder
  • 29,554
  • 19
  • 87
  • 130
  • 1
    STL containers manage their own memory. You can't create an array and have it manage some array that you allocated elsewhere. – krammer Jun 26 '12 at 10:24
  • 2
    Given that `std::array` and `std::vector` expect to manage their own memory, you should be very careful about using `reinterpret_cast` without taking steps to enure they don't try and delete data that isn't under their control. But that aside... don't be afraid of `memcpy`. Its a fairly efficient routine, after all. – Rook Jun 26 '12 at 10:25
  • Ok thanks. I want to do this safely, not do a hack, I just wondered if it was possible is all :) – jcoder Jun 26 '12 at 10:27
  • 3
    @krammer `std::array` is an aggregate, not officially a container. It has no dynamically allocated memory so the memory management is trivial. – juanchopanza Jun 26 '12 at 10:30
  • I agree @Juanchopanza, I would go so far as to say it can be identical. To krammer and Rook: std::array is very different from std::vector, std::array will NOT reallocate memory, it's size is hard coded – Michael Feb 07 '18 at 23:13

3 Answers3

26

As discussed in this post Is std::array<T, S> guaranteed to be POD if T is POD?

std::array<int, N> is POD and thus standard layout. As far as I understand the standard layout requirements, this means that the pointer to the object is identical to the pointer to the first member. Since std::array has no private/ protected members (according to http://en.cppreference.com/w/cpp/container/array), this should agree with the first element in the wrapped array. Thus something like

reinterpret_cast< std::array<int, 5>* >( &data )

is in my opinion guaranteed to work by the standard. I have to admit, though, that I sometimes have difficulties interpreting the standard language, so please correct me if I am wrong.

Regards Claas

Community
  • 1
  • 1
Claas
  • 389
  • 1
  • 3
  • 5
  • 3
    I think you are correct but I would check that array5 * p=reinterpret_cast< array5* >( &data ); ASSERT(p->data() == &data); wich is what we finally want – CoffeDeveloper Jul 03 '14 at 11:44
13

You can use reinterpret_cast, however note that it's an ugly dirty hack and you should not do something like this in your real release code:

std::array<int, 5> &a = reinterpret_cast<std::array<int, 5>&>(data);

The problems might arise if the internal implementation of std::array changes (e.g. some additional fields will be added in the debug version of STL to do some runtime checks). Then this code will start crashing without any informative messages (as it's based on an implicit assumption that std::array object and a C array have the same memory layout).

If you decide to go for the ugly dirty hack nonetheless, at least add a compile-time size check:

    C_ASSERT(sizeof(a) == sizeof(data));

This will produce an error in case the size of std::array<> stops matching the size of your C array (due to some changes in the STL implementation).

Ivan Shcherbakov
  • 2,063
  • 14
  • 23
  • Yeah, I got this to "work". I just wondered if there was a way that wasn't an ugly dirty hack :) thanks – jcoder Jun 26 '12 at 10:29
  • 4
    The name `reinterpret_cast` is intentionally ugly to make clear that an ugly hack is being used. – MSalters Jun 26 '12 at 10:35
  • Thanks, good answer. I won't go with a dirty hack. There was just a case in my code where I had a fixed sized array so wanted to use std::array throughout my code, but old code that I can't change gave me a c style array. I won't do a hack for it, but it would have been nice to be able to do something efficient and supported. – jcoder Jun 26 '12 at 10:54
  • If you tell us what are you trying to accomplish with using std::array (use iterators? algorithms?), we might suggest a non-hack way. – Ivan Shcherbakov Jun 26 '12 at 11:02
  • 3
    @JohnB C++ specifies that the type `std::complex` is layout compatible with C `_Complex` types by saying things like "the expression `reinterpret_cast(z)` shall be well-formed, `reinterpret_cast(z)[0]` shall designate the real part of z," etc. So this sort of thing is not without precedent. `std::array` isn't required to be layout compatible with raw arrays (e.g., `std::array` may have extra members, in which case what you want is actually impossible), but if it does happen to be layout compatible then I'd say this code is well-formed, if ugly, and not a dirty hack. – bames53 Jun 26 '12 at 15:12
  • It is layout-compatible for now. But you don't want the version 10 of your program to suddenly start crashing because the new implementation of STL has some members added and an assumption you made 5 years ago and don't remember does not hold any more. That could cost days of painful debugging... – Ivan Shcherbakov Jun 26 '12 at 16:06
  • I suppose I could write my own array style template that supports begin() end() and size() etc that is layout compatible... Hmmm – jcoder Jun 26 '12 at 16:20
  • 5
    @bames53 This use of `reinterpret_cast` should probably be prefaced with something like `static_assert(sizeof(data) == sizeof(std::array), "std::array isn't layout-compatible with T[N].");`. That would provide a clear error message indicating the issue, effectively future-proofing (and weird-compiler-proofing) the cast. – Justin Time - Reinstate Monica Dec 15 '16 at 19:56
  • Bonus question: If I have `std::vector> va3`, is `reinterpret_cast(va3.data())` pointing to a valid buffer of `3 * va3.size()` `int`s? Likewise, for `std::vector vi, is `reinterpret_cast>(vi.data())` pointing to a buffer of `vi.size() / 3` `std::array`s? – Ben Dec 04 '20 at 13:33
12

You cannot do that. The std::array is an aggregate and holds its own block of data (as opposed to a pointer to a block of data that can be easily reassigned). So there's no way of avoiding a copy of all the elements. In C++11 this is particularly important because the array's data cannot be moved, so there's no efficient std::swap function, for example.

Stephane Rolland
  • 38,876
  • 35
  • 121
  • 169
juanchopanza
  • 223,364
  • 34
  • 402
  • 480
  • 7
    I understand that. I don't want to create a new std::array with it's own data, I want to refer to the existing data as if it was an std::array as they are likely the same layout. If it's possible to do safely that is. I'm sure a reinterpret_cast "hack" would "work"... – jcoder Jun 26 '12 at 10:24
  • upvoted anyway as "sorry you can't do that safely" is a good answer to the question even if not what I hoped for. – jcoder Jun 26 '12 at 10:28