将结构转换为数组

Jon*_*Mee 2 c++ arrays alias struct reinterpret-cast

这是一个严格的别名问题,因为编译器会因此引起任何优化顺序问题。

假设我float在一个中有三个public struct XMFLOAT3(与这个不同)。我想强制转换成一个float*。这会使我陷入优化麻烦吗?

XMFLOAT3 foo = {1.0f, 2.0f, 3.0f};
auto bar = &foo.x;

bar[2] += 5.0f;
foo.z += 5.0f;
cout << foo.z;
Run Code Online (Sandbox Code Playgroud)

我认为这将始终打印“ 13”。但是这段代码呢:

XMFLOAT3 foo = {1.0f, 2.0f, 3.0f};
auto bar = reinterpret_cast<float*>(&foo);

bar[2] += 5.0f;
foo.z += 5.0f;
cout << foo.z;
Run Code Online (Sandbox Code Playgroud)

我认为这是合法的,因为根据http://en.cppreference.com/w/cpp/language/reinterpret_cast#Type_aliasing

T2是聚合类型或联合类型,其将上述类型之一作为元素或非静态成员(递归地包括子聚合的元素和所包含的联合的非静态数据成员):这样可以安全地进行强制转换从结构的第一个成员到联合的元素,再到包含它的结构/联合。

我对此的理解正确吗?

显然,这将成为依赖的声明的实现XMFLOAT3

Jon*_*ely 5

reinterpret_castXMFLOAT3*float*是确定的,因为:

9.2 [class.mem]第20段:

如果标准布局类对象具有任何非静态数据成员,则其地址与其第一个非静态数据成员的地址相同。否则,其地址与其第一个基类子对象(如果有)的地址相同。[ 注意:因此,在标准布局结构对象中可能会存在未命名的填充,但在其开始处可能没有,这是实现适当对齐所必需的。— 尾注 ]

这意味着第一个成员的地址就是该结构的地址,并且*bar在访问时不涉及别名,因为您可以float通过类型为的左值访问a float,这很好。

但是强制转换也是不必要的,它等效于第一个版本:

auto bar = &foo.x;
Run Code Online (Sandbox Code Playgroud)

bar[2]仅当结构的成员之间没有填充时,或更确切地说,如果数据成员的布局与数组相同,该表达式才可以float[3],在这种情况下,3.9.2 [basic.compound]第3段表示可以:

对象指针类型的有效值表示内存中字节的地址(1.7)或空指针(4.10)。如果类型的对象T位于地址处A,则将以其值为值的cv 类型的指针指向该对象,而不管如何获取该值。T*A

实际上,没有理由不会将三个相同类型的相邻非静态数据成员与一个数组完全相同地放置(并且我认为Itanium ABI可以保证),但是为了安全起见,您可以添加:

 static_assert(sizeof(XMFLOAT3)==sizeof(float[3]),
     "XMFLOAT3 layout must be compatible with float[3]");
Run Code Online (Sandbox Code Playgroud)

或偏执狂,或者在之后仅还有其他成员z

 static_assert(offsetof(XMFLOAT3, y)==sizeof(float)
               && offsetof(XMFLOAT3, z)==sizeof(float)*2,
     "XMFLOAT3 layout must be compatible with float[3]");
Run Code Online (Sandbox Code Playgroud)

显然,这将成为依赖XMFLOAT3声明的实现。

是的,它依赖于它是标准布局类类型,并且依赖于它的数据成员的顺序和类型。