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):
- const_cast
- static_cast
- static_cast,后跟const_cast
- reinterpret_cast
- 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_cast将int&转换为unsigned long&. const_cast和static_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()中a和b的定义顺序,结果将变为相等(在具有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.