【问题标题】:reinterpret_cast bug or UB? [duplicate]reinterpret_cast 错误还是 UB? [复制]
【发布时间】:2019-02-21 18:00:53
【问题描述】:

考虑以下代码:

#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) 错误?

【问题讨论】:

标签: c++ language-lawyer undefined-behavior reinterpret-cast


【解决方案1】:

这是 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&lt;std::uintptr_t, std::uintptr_t&gt;。或者完全避开这对和return std::min(...);


就语言细节而言,由于[expr.reinterpret.cast]/4,您可以将指针转换为足够大的整数类型,并且std::uintptr_t 保证足够大。所以一旦你解决了悬空参考问题,你就没事了。

【讨论】:

  • Minimal fix。我们真的需要 C++ 中的生命周期依赖标记。
  • @Yakk-AdamNevraumont 自定义衰减机制也会很有趣,这样我们就可以在此处指定 pairpair&lt;T,T&gt; 而不是 pair&lt;T const&amp;, T const&amp;&gt;
  • 这是其中一件事,回想起来似乎很明显,但也很容易忘记一次,结果是致命的。 ://
  • @Barry std::pair&lt; T const&amp;, T const&amp; &gt; minmax( T const&amp; [[lifetime]], T const&amp; [[lifetime]] ) (这里的属性可能不合适)它指出返回值的有效生命周期取决于两个输入参数的生命周期。然后编译器可以 (a) 生命周期扩展绑定到输入参数的临时对象,或 (b) 拒绝使用悬空引用作为不安全。返回类型的变化取决于调用上下文,这似乎比这种标记更难。
  • 我希望看到编译器对此发出警告
【解决方案2】:

reinterpret_cast 定义明确。问题是 the type of const auto pairconst std::pair&lt;const std::uintptr_t&amp;, const std::uintptr_t&amp;&gt; 因为这是 std::minmax 返回的,所以你有悬空引用。

您只需要摆脱悬空引用即可使其工作:

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

【讨论】:

    猜你喜欢
    • 2015-05-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-17
    • 1970-01-01
    • 2011-05-27
    • 1970-01-01
    • 2012-08-27
    相关资源
    最近更新 更多