我正在转换一堆代码以使用C ++风格的转换(在-Wold-style-cast
的帮助下)。我并没有完全出售它用于原始变量,但我一般都是C ++风格的演员。
某些字节序转换代码中出现一个问题。当前代码如下所示:
#define REINTERPRET_VARIABLE(VAR,TYPE) (*((TYPE*)(&VAR)))
//...
uint16_t reverse(uint16_t val) { /*stuff to reverse uint16_t*/ }
int16_t reverse( int16_t val) {
uint16_t temp = reverse(REINTERPRET_VARIABLE(val,uint16_t));
return REINTERPRET_VARIABLE(temp,int16_t);
}
现在,endianness并不关心签名。因此,要反转int16_t
,我们可以将其视为与uint16_t
完全相同的目的。这表示这样的代码:
int16_t reverse( int16_t val) {
return reinterpret_cast<int16_t>(reverse(reinterpret_cast<uint16_t>(val)));
}
但是,如this中所述,特别是this问题,reinterpret_cast
需要引用或指针(除非它自己投射)。这表明:
int16_t reverse( int16_t val) {
return reinterpret_cast<int16_t&>(reverse(reinterpret_cast<uint16_t&>(val)));
}
这不起作用,因为正如我的编译器告诉我的那样,外部演员想要一个左值。要解决此问题,您需要执行以下操作:
int16_t reverse( int16_t val) {
uint16_t temp = reverse(reinterpret_cast<uint16_t&>(val));
return reinterpret_cast<int16_t&>(temp);
}
这与原始代码没什么不同,实际上临时变量存在的原因相同,但我提出了四个问题:
reinterpret_cast
甚至需要临时的?我可以理解一个愚蠢的编译器需要一个临时的来支持REINTERPRET_VARIABLE
的指针nastiness,但是reinterpret_cast
应该只是重新解释位。这与RVO有什么冲突吗?reinterpret_cast
看起来像是在返回引用。由于函数返回值不是引用,我很确定这没关系;返回值将是副本,而不是引用。但是,我还是想知道什么样的参考甚至真的意味着?在这种情况下 是合适的,对吧?reinterpret_cast
会更快,因为编译器不需要弄清楚应该重新解释这些位 - 我只是告诉它他们应该?答案 0 :(得分:2)
由于两年内没有人用语言证据来回答这个问题,我会用经过深思熟虑的猜测来回答这个问题。
谁知道呢。但正如你所推测的那样,这显然是必要的。为避免issues with strict aliasing,最安全的方法是使用memcpy
,任何编译器都会对其进行正确优化。
任何此类问题的答案始终是对其进行分析并检查反汇编。在你给出的例子中,例如GCC will optimize it to:
reverse(short):
mov eax, edi
rol ax, 8
ret
这看起来非常优秀(mov
用于从输入寄存器进行复制;如果你内联你的功能并使用它,你会发现它完全没有。)
这是一个语言律师问题。可能有一些有用的语义含义。别担心。你从那以后就没有写过这样的代码。
再次,个人资料。也许重新解释铸造会妨碍某些优化。您应该遵循与上面提到的严格别名相同的准则。
答案 1 :(得分:1)
temp
是必需的,因为生成返回值的强制转换需要左值,如您所发现的那样。
我希望编译器能够优化它。
reinterpret_cast<T&>(x)
与* reinterpret_cast<T *>(&x)
相同,它是指定与x
占用相同内存位置的左值。请注意,表达式的类型永远不是引用;但转换为T&
或使用*
运算符的结果是左值。
我不希望出现任何性能问题。
此特定代码段没有严格的别名问题,因为允许将整数类型别名为同一类型的有符号或无符号变体。但是你建议代码库充满重新解释强制转换,所以你应该留意其他地方严格的别名冲突,也许用-fno-strict-aliasing
编译直到它被整理出来。