C++ strict-aliasing agnostic cast

C++ strict-aliasing agnostic cast

我在 Stack Overflow 中阅读了很多关于严格别名的 QA,但它们都很常见,而且讨论总是倾向于参考 C++ 标准的深层细节,这些细节几乎总是难以正确理解。特别是当标准不直接说,而是用一种含糊不清的方式描述某些东西时。 所以,我的问题可能与这里的大量 QA 重复,但是请只回答一个具体问题:

这样做 "nonalias_cast" 是正确的方法吗?:

template<class OUT, class IN>
inline auto nonalias_cast(IN *data) {
    char *tmp = reinterpret_cast<char *>(data);
    return reinterpret_cast<OUT>(tmp);
}

float f = 3.14;
unsigned *u = nonalias_cast<unsigned *>(&f);
*u = 0x3f800000;
// now f should be equal 1.0

我想答案是。但是有什么好的解决方法吗?当然,除了禁用严格别名标志。联合也不是一个方便的选择,除非有一种方法可以在 nonalias_cast 函数体内安装联合 hack。 memcpy 在这里也不是一个选项 - 数据更改应该是同步的。

不可能实现的梦想还是难以捉摸的现实?

更新:

好的,既然我们在 "is it possible?" 问题上得到了否定的答案,我想问你一个困扰我的额外问题:

如何解决这个任务?我的意思是有很多实际任务需要 "play with a bits" 方法。例如,假设您必须编写一个 IEEE-754 浮点转换器,如 this。我更关心问题的实际方面:如何有一个解决方法来达到目标​​?至少 "pain in @#$" 方式。

Is it a correct way to do a "nonalias_cast"?

没有

But is there any nice workaround?

再一次,没有。

两者的原因很简单,&f 不是某个类型 unsigned int 对象的地址,并且对指针进行再多的转换也不会改变这一点。

不,你的nonalias_cast没有用,也不能用。

类型别名规则不是(直接)关于转换指针。事实上,none 的转化具有未定义的行为。规则是关于通过另一种类型的指针访问某种类型的对象。

无论你如何转换指针,指向的对象仍然是一个float对象,通过unsigned指针访问它违反了类型别名规则。


An impossible dream or an elusive reality?

在标准 C++ 中,这是不可能的。

正如其他答案正确指出的那样:这是不可能的,因为您不允许通过 unsigned 指针访问 float 对象,并且没有强制转换会删除该规则.

那么你是如何解决这个问题的?不要通过 unsigned 指针访问对象!使用 float*char* 来传递对象,因为它们是在严格别名下唯一允许的指针类型。然后,当您实际需要在 unsigned 语义下访问对象时,您可以执行从 float* 到本地 unsignedmemcpy(一旦您返回 memcpy完成)。你的编译器 will be smart enough to generate efficient code for this.

请注意,这意味着您的界面上到处都是 float* 而不是 unsigned*。这正是使它起作用的原因:类型系统始终知道正确的数据类型。如果您尝试通过类型系统将 float 作为 unsigned* 走私,事情只会开始崩溃,您希望首先同意这是一个可疑的想法。