r值参考铸造和临时物化

Sup*_*sor 11 c++ language-lawyer c++11

下面代码的输出产生:

void doit(const T1 &, const T2 &) [T1 = unsigned long, T2 = int]
t1 == t2
t1 == (T1)t2
t1 != (T1&)t2
t1 == (T1&&)t2
Run Code Online (Sandbox Code Playgroud)

我知道这个t1 == t2案子只是一个不可或缺的促销活动.

第二种情况t1 == (T1)t2是相同的,只是明确的.

第三种情况t1 == (T1&)t2必须是reinterpret_cast某种形式......但是,进一步的解释会有所帮助.

第四种情况t1 == (T1&&)t2是我坚持的.我在问题的标题中加入了"临时实现"这个术语,因为这是我能得到某种答案的最接近的.

有人可以查看这四个案例吗?

码:

#include <iostream>    

template <typename T1, typename T2>
void doit(const T1& t1, const T2& t2) {
  std::cout << __PRETTY_FUNCTION__ << '\n';

  if (t1 == t2) {
    std::cout << "t1 == t2" << '\n';
  }
  else {
    std::cout << "t1 != t2" << '\n';
  }    

  if (t1 == (T1)t2) {
    std::cout << "t1 == (T1)t2" << '\n';
  }
  else {
    std::cout << "t1 != (T1)t2" << '\n';
  }    

  if (t1 == (T1&)t2) {
    std::cout << "t1 == (T1&)t2" << '\n';
  }
  else {
    std::cout << "t1 != (T1&)t2" << '\n';
  }    

  if (t1 == (T1&&)t2) {
    std::cout << "t1 == (T1&&)t2" << '\n';
  }
  else {
    std::cout << "t1 != (T1&&)t2" << '\n';
  }
}    

int main() {
  const unsigned long a = 1;
  const int b = 1;    

  doit(a, b);    

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

Swi*_*Pie -2

从技术上讲,所有四种变体都依赖于平台

t1 == t2

t2 被提升为 'long',转换为unsigned long.

t1 == (T1)t2

signed int被转换为其表示形式为unsigned long,以便被强制转换为long第一个。对于是否必须将其视为 UB 存在争议,因为结果取决于平台,老实说,除了 C99 标准中的条款之外,我真的不确定现在如何定义它。比较结果将与 相同t1 == t2。

t1 == (T1&)t2

转换结果是一个引用,您将引用(本质上是一个指针)与 T1 进行比较,该操作会产生未知的结果。为什么?引用 js 指向不同的类型。

t1 == (T1&&)t2

转换表达式产生一个右值,因此您可以比较值,但其类型是右值引用。比较结果将与 相同t1 == t2。

附言。如果你使用这样的值,就会发生有趣的事情(取决于平台,本质上是 UB):

  const unsigned long a = 0xFFFFFFFFFFFFFFFF;
  const int b = -1;  
Run Code Online (Sandbox Code Playgroud)

输出可能是:

t1 == t2
t1 == (T1)t2
t1 == (T1&)t2  
t1 == (T1&&)t2
Run Code Online (Sandbox Code Playgroud)

这就是为什么在比较无符号和有符号值时应小心的原因之一,尤其是使用 < 或 > 时。您会对 -1 大于 1 感到惊讶。

ifunsigned long是 64 位 int 并且指针等于 64 位 int。-1 如果转换为无符号,则成为其二进制表示形式。本质上,b 将成为地址为 0xFFFFFFFFFFFFFFFF 的指针。