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;
}

Godbolt link

这是 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 足够大。所以一旦你解决了悬空引用问题,你就没事了。