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*
到本地 unsigned
的 memcpy
(一旦您返回 memcpy
完成)。你的编译器 will be smart enough to generate efficient code for this.
请注意,这意味着您的界面上到处都是 float*
而不是 unsigned*
。这正是使它起作用的原因:类型系统始终知道正确的数据类型。如果您尝试通过类型系统将 float
作为 unsigned*
走私,事情只会开始崩溃,您希望首先同意这是一个可疑的想法。
我在 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*
到本地 unsigned
的 memcpy
(一旦您返回 memcpy
完成)。你的编译器 will be smart enough to generate efficient code for this.
请注意,这意味着您的界面上到处都是 float*
而不是 unsigned*
。这正是使它起作用的原因:类型系统始终知道正确的数据类型。如果您尝试通过类型系统将 float
作为 unsigned*
走私,事情只会开始崩溃,您希望首先同意这是一个可疑的想法。