"转换为标准布局的第一个成员"类型惩罚规则是否扩展到数组?

Fra*_*ank 7 c++ arrays language-lawyer reinterpret-cast

具体来说,我在一个友好的C++包装器中包装一个C API.C API具有相当标准的形状:

struct foo {...};
void get_foos(size_t* count, foo* dst);
Run Code Online (Sandbox Code Playgroud)

而且我有什么喜欢做的,是通过传递一个类型化punned包装拯救自己的额外拷贝阵列直接到C API和一群健全检查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;
}
Run Code Online (Sandbox Code Playgroud)

我的理解是它是有效的代码,因为所有访问的内存位置都符合规则,但我想确认.

编辑:显然,只要reinterpret_cast<char*>(result.data() + n) == reinterpret_cast<char*>(result.data()) + n*sizeof(foo)是真的,它将在今天的所有主要编译器下工作.但我想知道标准是否同意.

Nic*_*las 5

首先,这不是打字.该reinterpret_cast你做的仅仅是在做的过书面的方式&result.data().foo_.类型punning通过指向另一种类型的指针/引用来访问一种类型的对象.您正在访问其他类型的子对象.

其次,这不起作用.指针算法基于具有数组(单个对象充当用于指针算术目的的1个元素的数组).并vector<T>通过平坦的定义,以产生阵列T秒.但是一个数组T并不等同于某个子对象的数组T,即使该子对象的大小TT标准布局相同.

因此,如果get_foos在其给定的foos 数组上执行指针运算,那就是UB.哦,当然,它几乎肯定会奏效.但语言的答案是UB.