这是一个严格的别名问题,因为编译器会导致任何优化顺序问题。
假设我在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
声明的实现。
答案 0 :(得分:4)
从reinterpret_cast
到XMFLOAT3*
的{{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]
,则指向类型为 cvT
的指针,其值为 地址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
。
如您所见,完全正常的优化器可能会产生“错误”的结果。