reinterpret_cast 错误或 UB?
reinterpret_cast bug or UB?
考虑以下代码:
#include <cstdint>
#include <algorithm>
std::uintptr_t minPointer(void *first, void *second) {
const auto pair = std::minmax(
reinterpret_cast<std::uintptr_t>(first),
reinterpret_cast<std::uintptr_t>(second)
);
return pair.first;
}
以及由 GCC8 生成的程序集加上 -O3
https://godbolt.org/z/qWJuV_ 对于 minPointer
:
minPointer(void*, void*):
mov rax, QWORD PTR [rsp-8]
ret
这显然不符合代码创建者的意图。此代码是否导致某些 UB 或 GCC(8) 错误?
reinterpret_cast
定义明确。问题是 the type of const auto pair
is const std::pair<const std::uintptr_t&, const std::uintptr_t&>
as that's what std::minmax
returns,所以你有悬空引用。
您只需删除悬空引用即可使其正常工作:
std::uintptr_t minPointer(void *first, void *second) {
const std::pair<std::uintptr_t, std::uintptr_t> pair = std::minmax(
reinterpret_cast<std::uintptr_t>(first),
reinterpret_cast<std::uintptr_t>(second)
);
return pair.first;
}
这是 UB,但不是你想的那样。
std::minmax()
的相关签名为:
template< class T >
std::pair<const T&,const T&> minmax( const T& a, const T& b );
在这种情况下,您的 pair
是对 uintptr_t const
的一对引用。我们引用的实际对象在哪里?没错,它们是在最后一行创建的临时对象,已经超出了范围!我们有悬而未决的参考。
如果您写道:
return std::minmax(
reinterpret_cast<std::uintptr_t>(first),
reinterpret_cast<std::uintptr_t>(second)
).first;
然后我们没有任何悬空引用,您可以看到 gcc 生成了适当的代码:
minPointer(void*, void*):
cmp rsi, rdi
mov rax, rdi
cmovbe rax, rsi
ret
或者,您可以将 pair
的类型明确指定为 std::pair<std::uintptr_t, std::uintptr_t>
。或者完全回避这对 return std::min(...);
.
就语言特性而言,由于 [expr.reinterpret.cast]/4,您可以将指针转换为足够大的整数类型,并且保证 std::uintptr_t
足够大。所以一旦你解决了悬空引用问题,你就没事了。
考虑以下代码:
#include <cstdint>
#include <algorithm>
std::uintptr_t minPointer(void *first, void *second) {
const auto pair = std::minmax(
reinterpret_cast<std::uintptr_t>(first),
reinterpret_cast<std::uintptr_t>(second)
);
return pair.first;
}
以及由 GCC8 生成的程序集加上 -O3
https://godbolt.org/z/qWJuV_ 对于 minPointer
:
minPointer(void*, void*):
mov rax, QWORD PTR [rsp-8]
ret
这显然不符合代码创建者的意图。此代码是否导致某些 UB 或 GCC(8) 错误?
reinterpret_cast
定义明确。问题是 the type of const auto pair
is const std::pair<const std::uintptr_t&, const std::uintptr_t&>
as that's what std::minmax
returns,所以你有悬空引用。
您只需删除悬空引用即可使其正常工作:
std::uintptr_t minPointer(void *first, void *second) {
const std::pair<std::uintptr_t, std::uintptr_t> pair = std::minmax(
reinterpret_cast<std::uintptr_t>(first),
reinterpret_cast<std::uintptr_t>(second)
);
return pair.first;
}
这是 UB,但不是你想的那样。
std::minmax()
的相关签名为:
template< class T > std::pair<const T&,const T&> minmax( const T& a, const T& b );
在这种情况下,您的 pair
是对 uintptr_t const
的一对引用。我们引用的实际对象在哪里?没错,它们是在最后一行创建的临时对象,已经超出了范围!我们有悬而未决的参考。
如果您写道:
return std::minmax(
reinterpret_cast<std::uintptr_t>(first),
reinterpret_cast<std::uintptr_t>(second)
).first;
然后我们没有任何悬空引用,您可以看到 gcc 生成了适当的代码:
minPointer(void*, void*):
cmp rsi, rdi
mov rax, rdi
cmovbe rax, rsi
ret
或者,您可以将 pair
的类型明确指定为 std::pair<std::uintptr_t, std::uintptr_t>
。或者完全回避这对 return std::min(...);
.
就语言特性而言,由于 [expr.reinterpret.cast]/4,您可以将指针转换为足够大的整数类型,并且保证 std::uintptr_t
足够大。所以一旦你解决了悬空引用问题,你就没事了。