У меня есть класс offset_ptr
, который работает как указатель, но хранит адрес памяти, на который он указывает, как смещение к своему собственному адресу this
.Вот версия со всем удаленным, которая не требуется для демонстрации проблемы:
template <typename T>
struct offset_ptr {
using offset_t = int64_t;
static constexpr auto const NULLPTR_OFFSET =
std::numeric_limits<offset_t>::max();
offset_ptr(T const* p)
: offset_{p == nullptr ? NULLPTR_OFFSET
: static_cast<offset_t>(
reinterpret_cast<uint8_t const*>(p) -
reinterpret_cast<uint8_t const*>(this))} {}
T* get() {
return
offset_ == NULLPTR_OFFSET
? nullptr
: reinterpret_cast<T*>(reinterpret_cast<uint8_t*>(this) + offset_);
}
offset_t offset_;
};
Этот код не работает с GCC -O2
и -O3
:
int* get() {
offset_ptr<int> ptr = static_cast<int*>(malloc(sizeof(int)));
auto p = ptr.get();
*p = 110; // WOW - please do not optimize me away :-(
return p;
}
(управление памятью и проверка ошибок намеренно опущены для простоты!)
Это также видно в сгенерированной сборке: https://godbolt.org/z/PfZEJM
Назначение просто отсутствует.
Как показано выше в ссылке на проводник компилятора Godbolt, он работает, когда
- назначенное значение используется непосредственно в самой функции
offset_ptr
находится в куче, а не в стеке - нет
offset_ptr
используется вообще
Работает для:
- Clang (си без оптимизации)
- MSVC (режим отладки и выпуска)
- GCC (текущие и старые версии)
-O0
и -O1
(но НЕ для -O2
и -O3
)
GCC и сборки Clang Address и UB sanitizer не указывают на какие-либо проблемы (кромеутечка памяти) при выполнении.
Может ли кто-нибудь указать на раздел в стандартном документе C ++, в котором говорится, что в этом коде есть UB (что может быть причиной для GCC, настойчиво оптимизирующего назначение)?Или это ошибка в GCC?
Редактировать: Удаление nullptr
проверок в offset_ptr
помогает (https://godbolt.org/z/5HjcLY). Но мне нужны эти нулевые проверки.