r值参考铸造和临时物化 [英] r-value Reference Casting and Temporary Materialization
问题描述
以下代码的输出产生:
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.
这篇关于r值参考铸造和临时物化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!