Почему я не могу переинтерпретировать uint в int?

вот что я хочу сделать:

const int64_t randomIntNumber = reinterpret_cast<int64_t> (randomUintNumber);

где randomUintNumber имеет тип uint64_t.

ошибка (MSVC 2010):

ошибка C2440: 'reinterpret_cast': не удается преобразовать из 'const uint64_t' преобразование "int64_t" 1> является допустимым стандартным преобразованием, что может быть выполнено неявно или с помощью static_cast, C-style cast или функция-стиль cast

почему он не компилируется? оба типы имеют одинаковую длину бита, не для этого ли предназначен reinterpret_cast?

6 ответов


потому что это не то, что reinterpret_cast для. Все разрешенные преобразования с reinterpret_cast включать указатели или ссылки, за исключением того, что целочисленный или перечислимый тип может быть reinterpret_cast для себя. Это все определено в стандарте,[expr.reinterpret.cast].

я не уверен, что вы пытаетесь добиться здесь, но если вы хотите randomIntNumber иметь то же значение, что и randomUintNumber, то ли

const int64_t randomIntNumber = randomUintNumber;

если это приводит к предупреждение компилятора, или если вы просто хотите быть более ясно, затем:

const int64_t randomIntNumber = static_cast<int64_t>(randomUintNumber);

результат приведения имеет то же значение, что и входные данные, если randomUintNumber меньше 263. В противном случае результат определяется реализацией, но я ожидаю всех известных реализаций, которые имеют int64_t определит его, чтобы сделать очевидную вещь: результат эквивалентен входному модулю 264.


если вы хотите randomIntNumber иметь тот же бит-шаблон, что и randomUintNumber, тогда вы можете сделать это:

int64_t tmp;
std::memcpy(&tmp, &randomUintNumber, sizeof(tmp));
const int64_t randomIntNumber = tmp;

С int64_t гарантированно использует представление дополнения two, вы бы Надежда, что реализация определяет static_cast чтобы иметь тот же результат, что и для значений вне диапазона uint64_t. Но на самом деле это не гарантируется стандартным AFAIK.

даже если randomUintNumber является константой времени компиляции, к сожалению, здесь randomIntNumber is не константа времени компиляции. Но тогда, как "random"-это время компиляции константа? ;-)

Если вам нужно обойти это, и вы не доверяете реализации, чтобы быть разумным о преобразовании вне диапазона неподписанных значений в подписанные типы, то что - то вроде этого:

const int64_t randomIntNumber = 
    randomUintNumber <= INT64_MAX ? 
        (int64_t) randomUintNumber :
        (int64_t) (randomUintNumber - INT64_MAX - 1) + INT64_MIN;

теперь, я за написание действительно портативного кода, где это возможно, но даже так я думаю, что это граничит с паранойей.


кстати, у вас может возникнуть соблазн написать это:

const int64_t randomIntNumber = reinterpret_cast<int64_t&>(randomUintNumber);

или:

const int64_t randomIntNumber = *reinterpret_cast<int64_t*>(&randomUintNumber);

этот не совсем гарантированно работает, потому что хотя где они существуют int64_t и uint64_t гарантированно являются типом со знаком и типом без знака одинакового размера, на самом деле они не гарантируются как подписанные и неподписанные версии стандартного целочисленного типа. Таким образом, это зависит от реализации, нарушает ли этот код строгое сглаживание. Код, нарушающий строгое сглаживание, имеет неопределенное поведение. Следующее делает не нарушить строгое сглаживание и нормально при условии, что битовый шаблон в randomUintNumber является допустимым представлением стоимостью long long:

unsigned long long x = 0;
const long long y = reinterpret_cast<long long &>(x);

так, на реализации, где int64_t и uint64_t являются typedefs для long long и unsigned long long, тогда мой reinterpret_cast ОК. И, как и в случае преобразования значений вне диапазона, определенных реализацией, в подписанные типы, вы бы ожидал что разумная вещь для реализаций - сделать их соответствующими подписанными / неподписанными типами. Так нравится static_cast и неявные преобразование, вы ожидаете, что он будет работать в любой разумной реализации, но на самом деле это не гарантируется.


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

никакое другое преобразование нельзя выполнить явно используя оператора reinterpret_cast.

In в вашем случае вам, вероятно, нужно преобразование типов, а не переинтерпретация существующего хранилища. Использовать static_cast для этой цели.


использовать static_cast в этих случаях. Я полагаю, что языковые дизайнеры во всей своей мудрости решили, что это не считается "достаточно опасным", чтобы гарантировать reinterpret_cast.


Из Стандарта C++11 (N3376) 5.2.10.1:

преобразования, которые могут быть выполнены явно с помощью reinterpret_cast перечислены ниже. Нет другое преобразование может быть выполнено явно с помощью reinterpret_cast.

единственное преобразование из интегрального типа в интегральный тип разрешено, если типы идентичны.

другие включают указатели.


reinterpret_cast используется для переинтерпретации хранилища объекта как другого объекта. Если вы не хотите проходить стандартную формулировку, Вы можете найти все это reinterpret_cast можно сделать здесь. Как правило, вы можете помнить, что вам нужно работать с типами указателей.

Итак, если вы действительно хотите переиначить битов, используемых для uint64_t as int64_t, то делаем так:

int64_t randomIntNumber = reinterpret_cast<int64_t&> (randomUintNumber);

однако, если вы просто хотите преобразовать объект, сохраняя его значение, если это возможно ... просто сделайте то, что предлагает компилятор, и используйте static_cast вместо.


Почему он не компилируется?

потому что ни один тип является указателем.

оба типа имеют одинаковую длину бита,

какое это имеет значение? Если бы они были указателями, возможно, имело бы значение, что они указывают на вещи одинакового размера, но они не указатели.

разве не для этого предназначен reinterpret_cast?

нет, reinterpret_cast для приведений указателей. Ты мог бы делать то, что ты ... хотите поставив & внутри броска и * вне его. Для этого и нужен актерский состав.