下面代码的输出产生:
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):
- const_cast
- 静态转换
- static_cast 后跟 const_cast
- 重新解释_cast
- reinterpret_cast 后跟 const_cast
解读(T1)t2
非常简单。 const_cast
失败,但是 static_cast
有效,所以它被解释为 static_cast<T1>(t2)
(上面的#2)。
对于 (T1&)t2
, 不可能转换 int&
至 unsigned long&
通过static_cast
.两者 const_cast
和 static_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
的定义顺序和 b
在main()
,结果将变为相等(在带有 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/