c++ - r 值引用转换和临时物化

标签 c++ c++11 language-lawyer

下面代码的输出产生:

void doit(const T1 &, const T2 &) [T1 = unsigned long, T2 = int]
t1 == t2
t1 == (T1)t2
t1 != (T1&)t2
t1 == (T1&&)t2

我理解 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;
}

最佳答案

编译器尝试按以下顺序将 c 风格的转换解释为 c++ 风格的转换(有关完整详细信息,请参阅 cppreference):

  1. const_cast
  2. 静态转换
  3. static_cast 后跟 const_cast
  4. 重新解释_cast
  5. reinterpret_cast 后跟 const_cast

解读(T1)t2非常简单。 const_cast失败,但是 static_cast有效,所以它被解释为 static_cast<T1>(t2) (上面的#2)。

对于 (T1&)t2 , 不可能转换 int&unsigned long&通过static_cast .两者 const_caststatic_cast失败,所以 reinterpret_cast最终使用,给出 reinterpret_cast<T1&>(t2) .准确地说,上面的#5,因为 t2 是常量:const_cast<T1&>(reinterpret_cast<const T1&>(t2)) .

编辑:static_cast对于 (T1&)t2由于 cppreference 中的关键行而失败: “如果转换可以通过多种方式解释为 static_cast 后跟 const_cast,则无法编译。”。涉及隐式转换,并且以下所有内容都是有效的(我假设至少存在以下重载):

  • T1 c1 = t2; const_cast<T1&>(static_cast<const T1&>(c1))
  • const T1& c1 = t2; const_cast<T1&>(static_cast<const T1&>(c1))
  • T1&& c1 = t2; const_cast<T1&>(static_cast<const T1&>(std::move(c1)))

注意实际的表达式,t1 == (T1&)t2 , 导致未定义的行为,正如 Swift 指出的那样(假设 sizeof(int) != sizeof(unsigned long) )。包含 int 的地址被视为(重新解释)持有 unsigned long .交换 a 的定义顺序和 bmain() ,结果将变为相等(在带有 gcc 的 x86 系统上)。由于错误 reinterpret_cast,这是唯一具有未定义行为的情况.其他情况定义明确,结果因平台而异。

对于 (T1&&)t2 , 转换来自 int (lvalue)unsigned long (xvalue) .一个xvalue本质上是一个 lvalue那是“可移动的”;它不是引用。转换为 static_cast<T1&&>(t2) (上面的#2)。转换相当于std::move((T1)t2) , 或 std:move(static_cast<T1>(t2)) .写代码的时候用std:move(static_cast<T1>(t2))而不是 static_cast<T1&&>(t2) ,因为意图更加明确。

这个例子展示了为什么应该使用 C++ 风格的转换而不是 C 风格的转换。 C++ 风格的强制转换的代码意图很明确,因为正确的强制转换是由开发人员明确指定的。对于 C 风格的强制转换,实际的强制转换由编译器选择,并可能导致令人惊讶的结果。

关于c++ - r 值引用转换和临时物化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48796854/

相关文章:

c++ - 如何在现代 C++ 中使用生成器初始化 const 容器?

c++ - 返回 CArray 的问题

c++ - 错误 : 'strstr' was not declared in this scope

c++ - 动态数组上基于范围的for循环?

c++ - 虚拟类的非虚拟版本

c++ - Lambda 自身返回 : is this legal?

c++ - 数组到指针的转换 + rvalue-ref : Overload resolution difference GCC vs clang

c++ - 声明模板的共享指针

c++ - 模板忽略 [[nodiscard]] 属性

c++ - =default 和没有参数的空构造函数之间的区别?