【问题标题】:reinterpret_cast rvalue and optimizationreinterpret_cast 右值和优化
【发布时间】:2014-09-05 18:38:10
【问题描述】:

我正在转换一堆代码以使用 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);
}    

现在,字节序不关心符号。因此,要反转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);
}

这和原来的代码差别不大,确实临时变量的存在也是同理,但是给我提了四个问题:

  1. 为什么reinterpret_cast 甚至需要一个临时的?我可以理解一个愚蠢的编译器需要一个临时的来支持REINTERPRET_VARIABLE 的指针肮脏,但reinterpret_cast 应该只是重新解释位。这是与 RVO 冲突还是什么?
  2. 要求该临时值是否会导致性能损失,或者编译器是否可以确定该临时值真的应该只是返回值?
  3. 第二个reinterpret_cast 看起来像是在返回一个引用。由于函数返回值不是参考,我很确定这没问题;返回值将是副本,而不是引用。但是,我仍然想知道强制转换到引用的真正含义是什么?在这种情况下合适的,对吧?
  4. 还有其他我应该注意的性能影响吗?我猜reinterpret_cast 会更快,因为编译器不需要弄清楚这些位应该被重新解释——我只是告诉他们应该?李>

【问题讨论】:

  • 如果您的编译器套件还没有为您提供一些字节交换函数/宏,我会感到惊讶... GCC 提供了一个名为 byteswap.h 的标头,其中包含 16/32/64-位字节交换实现。其他编译器可能会将它们放在不同的头文件中...
  • 确实,我遇到了同样的问题。我想知道你现在有什么想法吗?
  • @astroboyrx 查看新分配的答案。
  • 如果您可以尝试完全避免演员阵容会更好。

标签: c++ casting


【解决方案1】:
  1. temp 是必需的,因为&amp;(地址)运算符在下一行应用于它。此运算符需要一个左值(获取地址的对象)。

  2. 我希望编译器能够优化它。

  3. reinterpret_cast&lt;T&amp;&gt;(x)* reinterpret_cast&lt;T *&gt;(&amp;x) 相同,它是一个左值,指定与x 占用的内存位置相同。请注意,表达式的类型永远不是引用;但是转换为T&amp; 或使用* 运算符的结果是一个左值。

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

这段特定的代码没有严格的别名问题,因为它允许将整数类型别名为同一类型的有符号或无符号变体。但是您建议代码库中充满了重新解释转换,因此您应该留意其他地方是否存在严格的别名违规行为,也许可以使用-fno-strict-aliasing 进行编译,直到它被整理出来。

【讨论】:

  • 如果你能解释 为什么 1 是这样的,并从我的答案中添加关于分析的一点到 2,我会接受这个答案。回复:严格别名,请参阅here。仅当 cstdint typedefs 标准类型时才可以(可能为真,但也许不是)。
【解决方案2】:

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

  1. 谁知道呢。但这显然是必要的,正如您所推测的那样。为避免issues with strict aliasing,使用memcpy 是最安全的,任何编译器都会正确优化。

  2. 任何此类问题的答案始终是对其进行分析并检查反汇编。在您给出的示例中,例如海合会will optimize it to:

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

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

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

  4. 再说一遍,简介。也许重新解释强制转换会妨碍某些优化。您应该遵循与上述严格别名相同的准则。

【讨论】:

  • 感谢您添加此答案 :-) 当然,如果可能,memcpy 将是第一个选择。
猜你喜欢
  • 2019-10-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多