заменить reinterpret_cast на союзы в constexpr - хорошая идея? - PullRequest
0 голосов
/ 05 апреля 2019

Я использую этот код для преобразования указателя в size_t, он используется в функции ::std::hash, которая должна хешировать данный указатель во время компиляции, и поскольку reinterpret_cast s не разрешены в constexpr Я пришел к следующему решению, оно работает, как и ожидалось, но я хочу знать, можно ли считать это хорошей идеей. Также было бы здорово, если бы вы могли дать мне несколько советов о том, насколько переносим этот кусок кода на самом деле.

union {
   size_t datr;
   void* pointer;
} dummy{.pointer = (void*) somePointer};
size_t pointerAsAsize_t = dummy.datr; // how portable is this?

Есть ли лучшее решение - как указано выше, я хочу создать ::std::hash<some_pointer*>, который запускается во время компиляции.

Ответы [ 2 ]

4 голосов
/ 05 апреля 2019

Если вы попытаетесь выполнить это в контексте постоянного выражения (т.е. когда компилятор вынужден вызывать ваш код во время компиляции, например, выражение, приводящее к размеру массива или аргументу шаблона), вы обнаружите, что он не будет компилироваться. Выполнение Constexpr требует, чтобы все, что могло бы спровоцировать UB во время компиляции, было бы неправильно сформировано А поскольку ваш код провоцирует UB (путем доступа к неактивному члену объединения), он не сможет скомпилироваться.

Все три основных компилятора потерпят неудачу на этом в контексте постоянного выражения. MSVC (на удивление) выдает самое прямое сообщение об ошибке:

(13): ошибка C2131: выражение не было константой
(9): примечание: сбой был вызван доступом к неактивному члену профсоюза
(9): примечание: см. Использование 'foo :: Foo :: integer'

Ваш код, вероятно, "работает как положено" только потому, что вы вызываете его, когда компилятору не нужно выполнять его во время компиляции. Учитывая ошибки GCC / Clang, если вы использовали его в качестве размера массива для массива в стеке, он, вероятно, вызвал расширение языка массивов переменной длины GCC / Clang. Если вы отключите это, ваш код, скорее всего, не скомпилируется. Мой пример сделал это глобальным массивом, который не допускает VLA.


Есть ли лучшее решение

Неа. Даже C ++ 20 bit_cast не будет constexpr, если вы указали указатель (или тип, содержащий указатели) в качестве источника или места назначения. Указатели времени компиляции - это реальные вещи, а не просто числа, представляющие адреса; преобразование их в / из целых чисел просто нецелесообразно во время компиляции.

Указатели времени компиляции должны указывать на вещи, которые существуют во время компиляции. невозможно узнать, куда они укажут время выполнения . Так что идея хеширования значения указателя во время компиляции (даже указателя на статический / глобальный объект) просто обречена с самого начала.

0 голосов
/ 05 апреля 2019

Я думаю, что на большинстве платформ этот код будет работать, однако формально он не может.size_t - это тип без знака для хранения результата sizeof(xxx) constructrion.Нет гарантии, что он достаточно большой для хранения указателя.Вы можете добавить static_assert(sizeof(size_t) == sizeof(void*),"") или использовать std::intptr_t, если он доступен в вашей библиотеке.

...