36

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.

5
  • 1
    STL containers manage their own memory. You can't create an array and have it manage some array that you allocated elsewhere. Commented Jun 26, 2012 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. Commented Jun 26, 2012 at 10:25
  • Ok thanks. I want to do this safely, not do a hack, I just wondered if it was possible is all :) Commented Jun 26, 2012 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. Commented Jun 26, 2012 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 Commented Feb 7, 2018 at 23:13

4 Answers 4

28

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

Sign up to request clarification or add additional context in comments.

1 Comment

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
15

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).

9 Comments

Yeah, I got this to "work". I just wondered if there was a way that wasn't an ugly dirty hack :) thanks
The name reinterpret_cast is intentionally ugly to make clear that an ugly hack is being used.
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.
@JohnB C++ specifies that the type std::complex is layout compatible with C _Complex types by saying things like "the expression reinterpret_cast<cv T(&)[2]>(z) shall be well-formed, reinterpret_cast<cv T(&)[2]>(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 This use of reinterpret_cast should probably be prefaced with something like static_assert(sizeof(data) == sizeof(std::array<int, 5>), "std::array<T, N> 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.
|
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.

3 Comments

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"...
upvoted anyway as "sorry you can't do that safely" is a good answer to the question even if not what I hoped for.
What about reinterpret_cast? There is no requirement that the std::array be stored in a new variable.
0

The question is quite old but since std::array seems to be guaranteed to be POD, the checks before reinterpret_cast may not even be necessary.

Nevertheless, there are static checks to be super sure. There are the functions is_trivial and is_standard_layout to check a type for being POD at compile time. (is_pod is deprecated) Then, sizeof(data) == sizeof(std::array<int, size>) can also be checked at compile time as suggested above. In my use case, I want to create a const std::array& on-the-fly without always writing g(std::array{1,8,9}), when I want to forward some indices. I don't know if this interestes someone but here you go:

template<typename T,std::size_t size>
void g(const std::array<T,size>& a)
{
  //...
}

template<typename T,std::size_t size> 
void g(T const (&indices)[size])
{
    static_assert(std::is_standard_layout_v< std::array<T,size>>);
    static_assert(std::is_trivial_v< std::array<T,size>>);
    static_assert(sizeof(indices) == sizeof(std::array<T,size>),"std::array<T, N> isn't layout-compatible with T[N].");
    g(reinterpret_cast<const std::array<T,size>&>(indices));
}

int main()
{
    g({1,8,9});
}

Godbolt

Comments

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.