将Struct转换为数组

时间:2015-03-26 12:03:44

标签: c++ arrays struct alias reinterpret-cast

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

假设我在float中有三个公开struct XMFLOAT3(与this one不同。)我想要转换为float*。这会让我陷入优化困境吗?

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

bar[2] += 5.0f;
foo.z += 5.0f;
cout << foo.z;

我认为这将始终打印“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;

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

  

T2是聚合类型或联合类型,它将上述类型之一保存为元素或非静态成员(包括递归地,包含联合的子聚合和非静态数据成员的元素):这使得它安全地从结构的第一个成员和从联合的元素转换到包含它的结构/联合。

我对此的理解是否正确?

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

3 个答案:

答案 0 :(得分:4)

reinterpret_castXMFLOAT3*的{​​{1}}即可,原因如下:

9.2 [class.mem]第20段:

  

如果标准布局类对象具有任何非静态数据成员,则其地址与其第一个非静态数据成员的地址相同。否则,其地址与其第一个基类的地址相同   子对象(如果有的话)。 [注意:因此,标准布局结构对象中可能存在未命名的填充,但不是在其开头,以实现适当的对齐。 - 结束记录]

这意味着第一个成员的地址是结构的地址,当您访问float*时没有涉及别名,因为您通过类型{{1}的左值访问*bar这很好。

但演员阵容也是不必要的,它相当于第一个版本:

float

表达式float只有在结构成员之间没有填充时才可以,或者更确切地说,如果数据成员的布局与数组auto bar = &foo.x; 相同,其中案例3.9.2 [basic.compound]第3段说没关系:

  

对象指针类型的有效值表示内存中的字节地址(1.7)或空指针(4.10)。如果类型为bar[2]的对象位于地址float[3],则指向类型为 cv T的指针,其值为   地址A被称为指向该对象,无论该值是如何获得的。

实际上,没有理由相同类型的三个相邻的非静态数据成员不会与数组相同(我认为Itanium ABI保证它),但为了安全起见,您可以添加:< / p>

T*

或者是偏执狂,或者A之后只有其他成员:

 static_assert(sizeof(XMFLOAT3)==sizeof(float[3]),
     "XMFLOAT3 layout must be compatible with float[3]");
  

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

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

答案 1 :(得分:0)

完全有效;这与任何严格的别名无关。

严格的别名规则要求彼此别名的指针具有兼容的类型;
很明显,float*float*兼容。

答案 2 :(得分:-1)

考虑一个相当聪明的编译器:

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

bar[2] += 5.0f;
foo.z += 5.0f; // Since no previous expression referenced .z, I know .z==8.0
cout << foo.z; // So optimize this to a hardcoded cout << 8.0f

通过已知结果替换变量访问和操作是常见的优化。优化器在这里看到了.z的三种用法:初始赋值,增量和最终用法。它可以简单地确定这三个点的值,并替换它们。

由于结构成员不能重叠(与联盟不同),bar派生的.x不能重叠.z,因此.bar[2]不会影响.z

如您所见,完全正常的优化器可能会产生“错误”的结果。