r值参考铸造和临时物化 [英] r-value Reference Casting and Temporary Materialization

查看:148
本文介绍了r值参考铸造和临时物化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下代码的输出产生:

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

我了解到t1 == t2案只是一个整体提升.

I understand that the t1 == t2 case is simply an integral promotion.

第二种情况t1 == (T1)t2是同一件事,只是很明显.

The second case t1 == (T1)t2 is the same thing, just explicit.

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

The third case t1 == (T1&)t2 must be a reinterpret_cast of some sort... Though, further explanation would be helpful.

第四种情况t1 == (T1&&)t2是我所坚持的.我在问题的标题中加入了临时物化"一词,因为这是我能最接近某种答案的地方.

The fourth case t1 == (T1&&)t2 is what I am stuck on. I put in the term 'Temporary Materialization' in the question's title as this is the closest I could come to some sort of answer.

有人可以处理这四个案件吗?

Could someone go over these four cases?

代码:

#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 ++样式强制转换(请参阅

The compiler attempts to interpret c-style casts as c++-style casts, in the following order (see cppreference for full details):

  1. const_cast
  2. static_cast
  3. static_cast,后跟const_cast
  4. reinterpret_cast
  5. reinterpret_cast后跟const_cast

(T1)t2的解释非常简单. const_cast失败,但是static_cast起作用,因此将其解释为static_cast<T1>(t2)(上面的#2).

Interpretation of (T1)t2 is pretty straightforward. const_cast fails, but static_cast works, so it's interpreted as static_cast<T1>(t2) (#2 above).

对于(T1&)t2,不可能通过static_castint&转换为unsigned long&. const_caststatic_cast均失败,因此最终使用reinterpret_cast给出reinterpret_cast<T1&>(t2).确切地说,上面的#5是因为t2是const:const_cast<T1&>(reinterpret_cast<const T1&>(t2)).

For (T1&)t2, it's impossible to convert an int& to unsigned long& via static_cast. Both const_cast and static_cast fail, so reinterpret_cast is ultimately used, giving reinterpret_cast<T1&>(t2). To be precise, #5 above, since t2 is const: const_cast<T1&>(reinterpret_cast<const T1&>(t2)).

由于 cppreference :如果强制转换可以以多种方式解释为static_cast后跟const_cast,则无法对其进行编译." .涉及隐式转换,以下所有转换均有效(我假设至少存在以下重载):

The static_cast for (T1&)t2 fails due to a key line in cppreference: "If the cast can be interpreted in more than one way as static_cast followed by a const_cast, it cannot be compiled.". Implicit conversions are involved, and all of the following are valid (I assume the following overloads exist, at a minimum):

  • 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 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)))

请注意,正如斯威夫特指出的(假设sizeof(int) != sizeof(unsigned long)),实际表达式t1 == (T1&)t2导致未定义的行为.拥有int的地址被视为(重新解释)拥有unsigned long的地址.交换main()ab的定义顺序,结果将变为相等(在具有gcc的x86系统上).由于错误的reinterpret_cast,这是唯一具有未定义行为的情况.其他情况也有很好的定义,其结果是针对特定平台的.

Note that the actual expression, t1 == (T1&)t2, leads to undefined behavior, as Swift pointed out (assuming sizeof(int) != sizeof(unsigned long)). An address that holds an int is being treated (reinterpreted) as holding an unsigned long. Swap the order of definition of a and b in main(), and the result will change to be equal (on x86 systems with gcc). This is the only case that has undefined behavior, due to a bad reinterpret_cast. Other cases are well defined, with results that are platform specific.

对于(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),因为其意图更加清楚.

For (T1&&)t2, the conversion is from an int (lvalue) to an unsigned long (xvalue). An xvalue is essentially an lvalue that is "moveable;" it is not a reference. The conversion is static_cast<T1&&>(t2) (#2 above). The conversion is equivalent to std::move((T1)t2), or std:move(static_cast<T1>(t2)). When writing code, use std:move(static_cast<T1>(t2)) instead of static_cast<T1&&>(t2), as the intent is much more clear.

此示例说明了为什么应使用c ++样式强制转换而不是c样式强制转换. C ++样式的强制类型转换明确了代码意图,因为开发人员明确指定了正确的强制类型转换.使用c样式强制转换时,实际的强制转换由编译器选择,并可能导致令人惊讶的结果.

This example shows why c++-style casts should be used instead of c-style casts. Code intent is clear with c++-style casts, as the correct cast is explicitly specified by the developer. With c-style casts, the actual cast is selected by the compiler, and may lead to surprising results.

这篇关于r值参考铸造和临时物化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆