ошибка reinterpret_cast или 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)?

Ответ 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<std::uintptr_t, std::uintptr_t>. Или просто обойти пару целиком и return std::min(...); ,


Что касается особенностей языка, вам разрешено преобразовать указатель в достаточно большой тип интеграла из-за [expr.reinterpret.cast]/4, а std::uintptr_t гарантированно будет достаточно большим. Поэтому, как только вы исправите проблему оборванных ссылок, вы в порядке.

Ответ 2

reinterpret_cast четко определен. Проблема заключается в том, что тип const auto pair const std::pair<const std::uintptr_t&, const std::uintptr_t&> - это const std::pair<const std::uintptr_t&, const std::uintptr_t&> как то, что возвращает 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