7

What is the way to pass std::array<char, N> to such function:

template<size_t N>
void safe_func(char (&dest)[N]);

?

I try this one:

#include <array>

template <size_t N> using SafeArray = char[N];

template <size_t N> void safe_func(char (&dest)[N]) {}

int main() {
  SafeArray<10> a1;
  safe_func(a1);
  std::array<char, 10> a2;
  safe_func(*static_cast<SafeArray<10> *>(static_cast<void *>(a2.data())));
}

It works, but I doubt, may be something wrong with my cast, and on other compiler or platform (I used gcc/linux/amd64), I faced with wrong reference?

curiousguy
  • 8,038
  • 2
  • 40
  • 58
user1244932
  • 7,352
  • 5
  • 46
  • 103
  • I can't see this breaking but you might want to read through this: https://stackoverflow.com/questions/41463005/getting-reference-to-the-raw-array-from-stdarray – NathanOliver Jul 19 '18 at 13:08
  • you coud simplly reinterpret_cast the reference: It would be shorter and less error prone. But again this sort of programming is dangerous. If the element types and sizes of the types are not matched you won't notice, but the harm will be done. – Red.Wave Jul 19 '18 at 13:57

1 Answers1

8

One way:

template<class T, size_t N>
using c_array = T[N];

template<class T, size_t N>
c_array<T, N>& as_c_array(std::array<T, N>& a) {
    return reinterpret_cast<T(&)[N]>(*a.data());
}

int main() {
    std::array<int, 2> a;
    int(&b)[2] = as_c_array(a);
}

The standard requires that std::array is an aggregate and it's only member is T[N], a pointer to which std::array::data() returns. As the address of an aggregate coincides with the address of its first member, calling and dereferencing std::array::data() is not strictly necessary, reinterpret_cast<T(&)[N]>(a) works as well.

std::array originated in boost, where its sole purpose was to provide a standard container interface (begin/end/size/empty/etc.) to built-in arrays T[N] and nothing else, so that it doesn't have any overhead, 0-cost abstraction. Hence, you can basically cast boost::array<T, N> and T[N] back and forth albeit possibly breaking aliasing rules by doing so (the compiler assumes that boost::array<T, N> and T[N] refer to different objects, so you need to know how to cope with that in your specific case).

The standard dropped all the rationale and expressed the requirements of std::array in very weak and vague terms. So that people wonder whether it is truly only T[N] member there and not some allegedly extra-terrestrial type that satisfies the requirement.

Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
  • 2
    Minor nitpick: where does the standard say that `std::array` contains `T[N]`? – geza Jul 19 '18 at 15:29
  • 1
    @geza The standard says it is an aggregate and it is contiguous storage for multiple elements. Nothing fits that description apart from `T[N]`. – Maxim Egorushkin Jul 19 '18 at 15:32
  • @geza [The sole purpose of `std::array` is to provide a container-like interface to a c-style array.](https://www.boost.org/doc/libs/1_62_0/doc/html/array.html) – Maxim Egorushkin Jul 19 '18 at 16:39
  • 1
    Sure, but I didn't find in the standard that `std::array` contains `T[N]`. It could be anything, not just the usual types that's accessible (like, `NULL` is not defined, what it is exactly). But this is just nitpicking, as `std::array` surely implemented as `T[N]`, there is no point doing otherwise. – geza Jul 19 '18 at 18:02
  • 1
    @geza I explained in my first comment why given the standard requirements it cannot be anything but `T[N]`. You may like to specify another type that satisfies the requirements to support your _it could be anything_ claim. – Maxim Egorushkin Jul 19 '18 at 22:51
  • 1
    I love how this answer sums up literally everything about the history and MO of C++ in one fell swoop, whilst also answering the question in both a practical and legal manner. Top marks :D – Lightness Races in Orbit Jul 19 '18 at 23:24
  • @MaximEgorushkin: Technically it could recursively contain other structures that themselves [eventually] wrap a `T[N]` and nothing else, with no padding. But then that's effectively the same thing. So I don't know why I wrote this comment – Lightness Races in Orbit Jul 19 '18 at 23:25
  • @LightnessRacesinOrbit Yes, i thought about `array : array`. But why? – Maxim Egorushkin Jul 19 '18 at 23:25
  • @LightnessRacesinOrbit `T[0]` is not well-formed, while `array<0>` is though. – Maxim Egorushkin Jul 19 '18 at 23:26
  • 1
    @MaximEgorushkin: I always found it amusing that `T[0]` is not well-formed while `new T[0]` is, but then again I'm easily amused – Lightness Races in Orbit Jul 19 '18 at 23:28