interpret_cast右值和优化

reinterpret_cast rvalue and optimization

本文关键字:优化 cast interpret      更新时间:2023-10-16

我正在转换一堆代码以使用C++风格的强制转换(在-Wold-style-cast的帮助下)。我并不完全相信它对基元变量的使用,但我对C++风格的转换一般来说还是个新手。

在某些endian转换代码中出现一个问题。当前代码如下:

#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)));
}

然而,正如本问题中所描述的,特别是本问题中,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);
}

这与原始代码没有太大区别,事实上,临时变量的存在也是出于同样的原因,但我提出了四个问题:

  1. 为什么reinterpret_cast需要临时的?我可以理解一个愚蠢的编译器需要一个临时的来支持REINTERPRET_VARIABLE的指针脏,但reinterpret_cast应该只是重新解释位。这是与RVO冲突还是什么
  2. 要求临时值会导致性能损失吗?或者编译器可能会发现临时值实际上应该只是返回值吗
  3. 第二个reinterpret_cast看起来像是在返回一个引用。由于函数返回值不是引用,我确信这是可以的;返回值将是一个副本,而不是引用。然而,我仍然想知道铸造引用的真正含义是什么?在这种情况下是合适的,对吧
  4. 我是否应该意识到其他绩效影响?我想reinterpret_cast会更快,如果有什么不同的话,因为编译器不需要弄清楚比特应该被重新解释——我只是告诉它们应该重新解释
  1. temp是必需的,因为&(的地址)运算符在下一行应用于它。此运算符需要一个左值(要获取其地址的对象)。

  2. 我希望编译器能对它进行优化。

  3. reinterpret_cast<T&>(x)* reinterpret_cast<T *>(&x)相同,它是指定与x所占据的相同存储位置的左值。请注意,表达式的类型从来都不是引用;但是转换为T&或使用*运算符的结果是左值。

  4. 我预计不会有任何性能问题。

这段代码没有严格的别名问题,因为允许将整数类型别名为同一类型的有符号或无符号变体。但您建议代码库中充满了重新解释的强制转换,因此您应该注意其他地方的严格混叠冲突,也许可以使用-fno-strict-aliasing进行编译,直到它被解决为止。

由于两年来没有人用语言律师的事实来回答这个问题,我将用我有根据的猜测来回答。

  1. 谁知道呢。但正如你所推测的那样,这显然是必要的。为了避免严格混叠的问题,使用memcpy是最安全的,任何编译器都会对其进行正确优化。

  2. 任何此类问题的答案都是对其进行分析并检查拆卸情况。在您给出的示例中,例如GCC会将其优化为:

    reverse(short):
        mov     eax, edi
        rol     ax, 8
        ret
    

    这看起来非常优化(mov用于从输入寄存器复制;如果您内联函数并使用它,您会发现它完全不存在)。

  3. 这是一个语言律师的问题。可能有一些有用的语义。别担心。从那以后,你还没有写过这样的代码。

  4. 再次,简介。也许重新解释选角会妨碍某些优化。您应该遵循与上面提到的严格别名相同的准则。