reinterpret_cast rvalue和优化

时间:2014-09-05 18:38:11

标签: c++ casting

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

这与原始代码没什么不同,实际上临时变量存在的原因相同,但我提出了四个问题:

  1. 为什么reinterpret_cast甚至需要临时的?我可以理解一个愚蠢的编译器需要一个临时的来支持REINTERPRET_VARIABLE的指针nastiness,但是reinterpret_cast应该只是重新解释位。这与RVO有什么冲突吗?
  2. 是否会要求暂时性能下降,或者编译器是否可能发现临时应该只是返回值?
  3. 第二个reinterpret_cast看起来像是在返回引用。由于函数返回值不是引用,我很确定这没关系;返回值将是副本,而不是引用。但是,我还是想知道什么样的参考甚至真的意味着?在这种情况下 是合适的,对吧?
  4. 我应该注意其他任何性能影响吗?我猜测reinterpret_cast会更快,因为编译器不需要弄清楚应该重新解释这些位 - 我只是告诉它他们应该?

2 个答案:

答案 0 :(得分:2)

由于两年内没有人用语言证据来回答这个问题,我会用经过深思熟虑的猜测来回答这个问题。

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

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

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

    这看起来非常优秀(mov用于从输入寄存器进行复制;如果你内联你的功能并使用它,你会发现它完全没有。)

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

  4. 再次,个人资料。也许重新解释铸造会妨碍某些优化。您应该遵循与上面提到的严格别名相同的准则。

答案 1 :(得分:1)

  1. temp是必需的,因为生成返回值的强制转换需要左值,如您所发现的那样。

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

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

  4. 我不希望出现任何性能问题。

  5. 此特定代码段没有严格的别名问题,因为允许将整数类型别名为同一类型的有符号或无符号变体。但是你建议代码库充满重新解释强制转换,所以你应该留意其他地方严格的别名冲突,也许用-fno-strict-aliasing编译直到它被整理出来。