Обход ограничения reinterpret cast с помощью constexpr
В c++11, a constexpr
выражение не может содержать переиначить бросает. Например, если кто-то хочет манипулировать битами в числе с плавающей запятой, скажем, чтобы найти мантиссу числа:
constexpr unsigned int mantissa(float x) {
return ((*(unsigned int*)&x << 9) >> 9);
};
приведенный выше код не будет constexpr
. Теоретически я не вижу, как переинтерпретация в этом или подобных случаях может отличаться от арифметических операторов, но комплиер (и стандарт) этого не позволяют.
есть ли какой-либо умный способ получить вокруг этого ограничения?
2 ответов
я не вижу, как переинтерпретация в этом или подобных случаях может быть любой отличается от арифметических операторов
это не портативный.
вы, вероятно, знаете о том, что ваш код вызывает неопределенное поведение, так как вы разыменовываете указатель типа и, таким образом, нарушаете строгое сглаживание. Более того, поскольку C++14, операции, которые вызывали бы неопределенное поведение, больше не являются постоянными выражениями и поэтому должны создавать компилятор ошибка.
что вы пытаетесь сделать, это псевдоним float
объект с интегральным glvalue. Первый шаг-получить это glvalue; второй - выполнить преобразование lvalue в rvalue.
в C++14 первый шаг невозможно выполнить в постоянных выражениях. reinterpret_cast
категорически запрещено. И бросает в и из void*
, как static_cast<char const*>(static_cast<void const*>(&x))
, тоже не работает (N3797, [expr.const] / 2*):
- преобразование из типа cv
void *
к типу указатель-объект;
имейте в виду, что c-стиль бросания как (char*)
сводится к либо static_cast
или reinterpret_cast
чьи ограничения перечислены выше. (unsigned*)&x
поэтому сводится к reinterpret_cast<unsigned*>(&x)
и не работает.
в C++11 приведение к void const*
а потом char const*
не представляет проблемы (согласно стандарту; Clang все еще жалуется на последнее). Именующее-для-правосторонним значением преобразования тем не менее:
преобразование lvalue в rvalue (4.1), если оно не применяется к
- a glvalue Интеграл или тип перечисления, который относится к энергонезависимой объект const с предшествующей инициализацией, инициализированный постоянное выражение, или
- glvalue литерального типа, который ссылается на энергонезависимый объект, определенный с помощьюconstexpr
, или это относится к под-объект такого объекта, или
- glvalue литерального типа, который относится к энергонезависимый временный объект, срок службы которого не закончено, инициализировано постоянным выражением;
первые две пули не могут применяться здесь; ни один не имеет char
/unsigned
/ etc. объект был инициализирован precedingly, не мы определяем любой такой объект с constexpr
.
третья пуля также не применяется. Если мы напишем
char ch = *(char const*)(void const*)&x;
мы не создаем
ваш конкретный пример получения мантиссы float
number на самом деле довольно просто реализовать для цифры без каламбура вообще, и, таким образом, реализовать его в constexpr
моды. Единственная проблема будет, когда вы хотите взломать NaNs.
так как вы уже полагаетесь на float
являясь binary32
из IEEE 754 мы можем предположить то же самое, но по - другому-представить результаты. См. следующий код:
#include <limits>
constexpr float abs(float x) { return x<0 ? -x : x; }
constexpr int exponent(float x)
{
return abs(x)>=2 ? exponent(x/2)+1 :
abs(x)<1 ? exponent(x*2)-1 : 0;
}
constexpr float scalbn(float value, int exponent)
{
return exponent==0 ? value : exponent>0 ? scalbn(value*2,exponent-1) :
scalbn(value/2,exponent+1);
}
constexpr unsigned mantissa(float x)
{
return abs(x)<std::numeric_limits<float>::infinity() ?
// remove hidden 1 and bias the exponent to get integer
scalbn(scalbn(abs(x),-exponent(x))-1,23) : 0;
}
#include <iostream>
#include <iomanip>
#include <cstring>
int main()
{
constexpr float x=-235.23526f;
std::cout << std::hex << std::setfill('0');
// Show non-constexpr result to compare with
unsigned val; std::memcpy(&val,&x,sizeof val);
std::cout << std::setw(8) << (val&0x7fffff) << "\n";
// Now the sought-for constexpr result
constexpr auto constexprMantissa=mantissa(x);
std::cout << std::setw(8) << constexprMantissa << "\n";
}
посмотреть ее жить демо.