以下代码的输出产生:
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;
}
答案 0 :(得分:1)
编译器尝试按以下顺序将c样式转换解释为c ++样式转换(有关完整详细信息,请参阅cppreference):
对(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:const_cast<T1&>(reinterpret_cast<const T1&>(t2))
。
编辑:static_cast
的{{1}}由于cppreference中的关键行而失败:&#34;如果演员阵容可以多种方式解释为static_cast然后是const_cast,它无法编译。&#34; 。涉及隐式转换,以下所有内容都是有效的(我假设至少存在以下重载):
(T1&)t2
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)))
会导致未定义的行为,正如Swift指出的那样(假设为t1 == (T1&)t2
)。包含sizeof(int) != sizeof(unsigned long)
的地址正在被处理(重新解释)为持有int
。在unsigned long
中交换a
和b
的定义顺序,结果将更改为相等(在x86系统上使用gcc)。由于错误的main()
,这是唯一具有未定义行为的情况。其他案例定义明确,结果是特定于平台的。
对于reinterpret_cast
,转化是从(T1&&)t2
到int (lvalue)
。 unsigned long (xvalue)
基本上是xvalue
,可以移动;&#34;它不是一个参考。转换为lvalue
(上面的#2)。转化率相当于static_cast<T1&&>(t2)
或std::move((T1)t2)
。编写代码时,请使用std:move(static_cast<T1>(t2))
代替std:move(static_cast<T1>(t2))
,因为意图要清晰得多。
此示例说明了为什么应该使用c ++样式转换而不是c样式转换。使用c ++样式转换时代码意图很明显,因为开发人员明确指定了正确的转换。使用c样式转换,编译器会选择实际转换,并可能导致令人惊讶的结果。
答案 1 :(得分:-2)
从技术上讲,所有四种变体都是依赖于平台的
t1 == t2
t2升级为“long”,转换为unsigned long
。
t1 == (T1)t2
signed int
会转换为unsigned long
的表示形式,然后先将其转换为long
。如果必须将UB视为UB,则存在争议,因为结果取决于平台,我真的不确定它现在是如何定义的,除了C99标准中的条款。比较结果与t1 == t2
相同。
t1 == (T1&)t2
强制转换结果是一个引用,您将引用(实际上是指针)与T1进行比较,该操作会产生未知结果。为什么?引用js指向不同的类型。
t1 == (T1&&)t2
强制转换表达式产生一个右值,因此你做比较值,但它的类型是右值引用。比较结果与t1 == t2
相同。
PS。 如果您使用这样的值,有趣的事情发生(取决于平台,基本上是UB):
const unsigned long a = 0xFFFFFFFFFFFFFFFF;
const int b = -1;
输出可能是:
t1 == t2
t1 == (T1)t2
t1 == (T1&)t2
t1 == (T1&&)t2
这是为什么在将无符号值与有符号值进行比较时要小心的原因之一,尤其是&lt;或者&gt;。 -1会大于1,你会感到惊讶。
如果unsigned long
是64位int并且指针等于64位int。 -1如果转换为unsigned,则成为其二进制表示。本质上,b将成为地址为0xFFFFFFFFFFFFFFFF的指针。