Search code examples
c++arrayslanguage-lawyerreinterpret-cast

Does the "cast to first member of standard layout" type punning rule extend to arrays?


Specifically, I am wrapping a C API in a friendly C++ wrapper. The C API has this fairly standard shape:

struct foo {...};
void get_foos(size_t* count, foo* dst);

And what I'd like to do, is save myself an extra copy by passing a typed-punned wrapper array directly to the C api with a bunch of sanity checking static_assert().

class fooWrapper {
  foo raw_;
public:
   [...]
};

std::vector<fooWrapper> get_foo_vector() {
  size_t count = 0;
  get_foos(&count, nullptr);

  std::vector<fooWrapper> result(count);

  // Is this OK?
  static_assert(sizeof(foo) == sizeof(fooWrapper), "");
  static_assert(std::is_standard_layout<fooWrapper>::value, "");
  get_foos(&count, reinterpret_cast<foo*>(result.data()));

  return result;
}

My understanding is that it is valid code, since all accessed memory locations individually qualify under the rule, but I'd like confirmation on that.

Edit: Obviously, as long as reinterpret_cast<char*>(result.data() + n) == reinterpret_cast<char*>(result.data()) + n*sizeof(foo) is true, it'll work under all major compilers today. But I'm wondering if the standard agrees.


Solution

  • First, this is not type punning. The reinterpret_cast you're doing is just an over-written way of doing &result.data().foo_. Type punning is accessing an object of one type through a pointer/reference to another type. You're accessing a subobject of the other type.

    Second, this doesn't work. Pointer arithmetic is based on having an array (a single object acts as an array of 1 element for the purposes of pointer arithmetic). And vector<T> is defined by fiat to produce an array of Ts. But an array of T is not equivalent to an array of some subobject of T, even if that subobject is the same size as T and T is standard layout.

    Therefore, if get_foos performs pointer arithmetic on its given array of foos, that's UB. Oh sure, it will almost certainly work. But the language's answer is UB.