Проверка расположения памяти указателя во время компиляции: сбой объединения в constexpr
Рассмотрим следующий код:
// Preamble
#include <limits>
#include <iostream>
#include <type_traits>
// Gives an unsigned int of the given size in bytes, if it exists
template <std::size_t N>
struct uint_of_sizeof
{
using type = std::conditional_t<
N == sizeof(unsigned char),
unsigned char,
std::conditional_t<
N == sizeof(unsigned short int),
unsigned short int,
std::conditional_t<
N == sizeof(unsigned int),
unsigned int,
std::conditional_t<
N == sizeof(unsigned long long int),
unsigned long long int,
std::conditional_t<
N == sizeof(std::size_t),
std::size_t,
std::conditional_t<
N == sizeof(unsigned long int),
unsigned long int,
void
>
>
>
>
>
>;
};
// Allows to reinterpret pointer as an unsigned integral value
template <class T>
union pointer_converter
{
using pointer_type = T*;
using value_type = typename uint_of_sizeof<sizeof(pointer_type)>::type;
pointer_type pointer;
value_type value;
};
// Returns whether the pointer seems to have a normal layout
template <class T>
constexpr bool check_pointer_layout()
{
// Initialization
constexpr std::size_t alignment = alignof(T);
pointer_converter<T> converter0{nullptr};
pointer_converter<T> converter1{nullptr};
std::ptrdiff_t difference = 0;
bool ok = true;
// Checks zero and first increment
converter1.value = alignment;
difference = converter1.pointer - converter0.pointer;
ok = converter0.value == 0 && difference == 1;
// Checks shifts
while (ok && converter1.value < converter1.value << 1) {
ok = converter1.pointer - converter0.pointer == difference;
converter1.value <<= 1;
difference <<= 1;
}
// Finalization
return ok;
}
// Main function
int main(int argc, char* argv[])
{
/*constexpr*/ bool is_classical_layout = check_pointer_layout<int>(); /* HERE */
std::cout << is_classical_layout << std::endl;
return 0;
}
Этот код пытается проверить (очень наивно, я знаю), закодированы ли указатели в памяти, как можно предположить, и могут ли они быть более или менее безопасно переосмыслены как целые числа. Однако проблема в том, что constexpr
в этом контексте не работает.
pointer_layout.cpp: 77: 68: ошибка: доступ к элементу pointer_converter:: pointer вместо инициализированного члена pointer_converter:: value в константном выражении constexpr bool is_classical_layout = check_pointer_layout();
Вопрос 1: Есть ли способ сделать это (или что-то подобное) во время компиляции?
Вопрос 2: для этого лучше использовать во время выполнения unions
или же reinterpret_cast
или оба эквивалентны?