Когда использовать reinterpret_cast?

Меня немного смущает применимость reinterpret_cast против static_cast. Из того, что я прочитал, общие правила - использовать статическое приведение, когда типы можно интерпретировать во время компиляции, отсюда и слово static. Это приведение, которое компилятор C ++ использует также для неявных приведений.

reinterpret_casts применимы в двух сценариях:

  • преобразовать целочисленные типы в типы указателей и наоборот
  • преобразовать один тип указателя в другой. Общая идея, которую я получаю, заключается в том, что это непереносимо, и этого следует избегать.

Я немного смущен тем, что мне нужно, я вызываю C ++ из C, и код C должен удерживать объект C ++, поэтому в основном он содержит void*. Какое приведение следует использовать для преобразования между типом void * и типом класса?

Я видел использование как static_cast, так и reinterpret_cast? Хотя из того, что я читал, кажется, что static лучше, поскольку приведение может происходить во время компиляции? Хотя там сказано использовать reinterpret_cast для преобразования одного типа указателя в другой?


person HeretoLearn    schedule 21.02.2009    source источник
comment
reinterpret_cast не происходит во время выполнения. Оба они являются операторами времени компиляции. Из en.cppreference.com/w/cpp/language/reinterpret_cast: В отличие от static_cast, но, как и const_cast, выражение reinterpret_cast не компилируется ни в какие инструкции ЦП. Это чисто директива компилятора, которая инструктирует компилятор обрабатывать последовательность битов (представление объекта) выражения, как если бы она имела тип new_type.   -  person Cris Luengo    schedule 03.03.2017
comment
@HeretoLearn, можно ли добавить соответствующие фрагменты кода из файлов * .c и * .cpp? Думаю, это может улучшить постановку вопроса.   -  person OrenIshShalom    schedule 16.11.2017


Ответы (11)


Стандарт C ++ гарантирует следующее:

static_cast указатель на void* и обратно сохраняет адрес. То есть в дальнейшем a, b и c все указывают на один и тот же адрес:

int* a = new int();
void* b = static_cast<void*>(a);
int* c = static_cast<int*>(b);

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

int* a = new int();
void* b = reinterpret_cast<void*>(a);
int* c = reinterpret_cast<int*>(b);

a и c содержат одно и то же значение, но значение b не указано. (на практике он обычно содержит тот же адрес, что и a и c, но это не указано в стандарте, и это может быть неверно на машинах с более сложными системами памяти.)

Для преобразования в void* и обратно предпочтительнее использовать static_cast.

person jalf    schedule 21.02.2009
comment
Мне нравится, что "b" не определено. Это мешает вам делать с ним глупости. Если вы приводите что-то к другому типу указателя, вы задаетесь вопросом о проблемах, и тот факт, что вы не можете полагаться на него, делает вас более осторожными. Если бы вы использовали static_cast ‹› выше, зачем вообще нужна буква «b»? - person Martin York; 22.02.2009
comment
Я думал, что reinterpret_cast ‹› гарантирует тот же битовый шаблон. (что не то же самое, что действительный указатель на другой тип). - person Martin York; 22.02.2009
comment
@Martin - reinterpret_cast ‹› не гарантирует, что приведет к тому же битовому шаблону. Отображение, выполняемое reinterpret_cast ‹›, определяется реализацией. (С ++ 03 5.3.10). Однако в стандарте отмечается, что это неудивительно. - person Michael Burr; 30.04.2009
comment
значение b больше не является неопределенным в C ++ 11 при использовании reinterpret_cast. А в C ++ 03 преобразование int* в void* было запрещено с reinterpret_cast (хотя компиляторы не реализовали это, и это было непрактично, поэтому было изменено для C ++ 11). - person Johannes Schaub - litb; 29.10.2011
comment
хм, правда насчет переинтерпретации трансляции в / из void*. Интересно, почему я думал, что это законно, когда писал это. - person jalf; 29.10.2011
comment
Что будет, если смешать два состава? Это неопределенное поведение? stackoverflow.com/questions/16499683/ - person David; 11.05.2013
comment
На самом деле это не отвечает на вопрос, когда использовать reinterpret_cast. - person einpoklum; 15.04.2016
comment
@LokiAstari Я думаю, что неопределенность не мешает вам делать глупости. Это останавливает вас только тогда, когда вы вспомните, что это не указано. Огромная разница. Лично мне не нравится неопределенное. Слишком много для запоминания. - person Helin Wang; 30.01.2017
comment
@HelinWang Вот почему использование reinterpret_cast - это большой красный флаг в вашем коде, вы знаете, что это опасно, и он напоминает вам о проверке. static_cast не бросает такой большой и красный флаг и, следовательно, более опасен, потому что вам не предлагается вспомнить, что то, что у вас есть, действительно ужасно. - person Martin York; 31.01.2017
comment
static_cast отказывается преобразовывать указатели функций в void* и обратно. reinterpret_cast и C-стиль (void*) работают в этом случае. - person Binary Mind; 12.06.2017
comment
Пример, где значение b отличается, см. (Бесстыдный плагин) в моем ответе ниже. - person Avi Ginsburg; 15.09.2019
comment
static_casting указатель на void * и обратно сохраняет адрес. В интересах точности: некоторые типы указателей не могут быть безопасно и переносимо преобразованы в void * и обратно, независимо от того, какой тип преобразования используется. Это относится к любому типу указателя на функцию или типу указателя на функцию-член. isocpp.org/wiki/faq/ - person Max Barraclough; 13.12.2020

Один случай, когда reinterpret_cast необходим, - это взаимодействие с непрозрачными типами данных. Это часто происходит в API поставщиков, над которыми программист не властен. Вот надуманный пример, в котором поставщик предоставляет API для хранения и извлечения произвольных глобальных данных:

// vendor.hpp
typedef struct _Opaque * VendorGlobalUserData;
void VendorSetUserData(VendorGlobalUserData p);
VendorGlobalUserData VendorGetUserData();

Чтобы использовать этот API, программист должен преобразовать свои данные в VendorGlobalUserData и обратно. static_cast не сработает, нужно использовать reinterpret_cast:

// main.cpp
#include "vendor.hpp"
#include <iostream>
using namespace std;

struct MyUserData {
    MyUserData() : m(42) {}
    int m;
};

int main() {
    MyUserData u;

        // store global data
    VendorGlobalUserData d1;
//  d1 = &u;                                          // compile error
//  d1 = static_cast<VendorGlobalUserData>(&u);       // compile error
    d1 = reinterpret_cast<VendorGlobalUserData>(&u);  // ok
    VendorSetUserData(d1);

        // do other stuff...

        // retrieve global data
    VendorGlobalUserData d2 = VendorGetUserData();
    MyUserData * p = 0;
//  p = d2;                                           // compile error
//  p = static_cast<MyUserData *>(d2);                // compile error
    p = reinterpret_cast<MyUserData *>(d2);           // ok

    if (p) { cout << p->m << endl; }
    return 0;
}

Ниже представлена ​​надуманная реализация примера API:

// vendor.cpp
static VendorGlobalUserData g = 0;
void VendorSetUserData(VendorGlobalUserData p) { g = p; }
VendorGlobalUserData VendorGetUserData() { return g; }
person jwfearn    schedule 21.02.2009
comment
Да, это единственное, что я могу придумать для использования reinterpret_cast. - person jalf; 22.02.2009
comment
Это может быть запоздалый вопрос, но почему API поставщика не использует для этого void*? - person Xeo; 29.10.2011
comment
@Xeo Они не используют void *, потому что тогда они теряют (некоторую) проверку типов во время компиляции. - person jesup; 14.08.2014
comment
Практический вариант использования непрозрачных типов данных - это когда вы хотите предоставить API для C, но написать реализацию на C ++. ICU - это пример библиотеки, которая делает это в нескольких местах. Например, в API проверки спуфинга вы имеете дело с указателями типа USpoofChecker*, где USpoofChecker - это пустая структура. Однако внутри, когда вы передаете USpoofChecker*, он преобразуется reinterpret_cast во внутренний тип C ++. - person sffc; 23.03.2017
comment
@sffc, почему бы не предоставить пользователю тип структуры C? - person Gupta; 09.09.2018
comment
@hsalimi, потому что ICU4C двоично совместим с C API; вы можете заменить любую более новую версию библиотеки ICU4C, и ваша двоичная сборка на более старой версии будет работать. Предоставление структуры через общедоступный API означало бы, что мы никогда не могли бы добавлять новые поля в структуру, потому что структура компилируется в двоичный файл клиента. - person sffc; 14.09.2018
comment
Ваш пример - неопределенное поведение в C ++. Вы не можете повторно интерпретировать одну структуру в другую из-за строгих правил псевдонима типов. Единственная легальная форма этого - memcpy, и поэтому был изобретен C ++ 20 std :: bit_cast. - person jaskmar; 06.05.2020
comment
@jaskmar Но вы можете reinterpret_cast указатели. В этом весь трюк: код поставщика на самом деле никогда не пытается использовать значение VendorGlobalUserData, он только берет его из пользовательского кода и передает его обратно. А пользовательский код точно знает правильный тип указателя, поэтому он может reinterprete_cast вернуться и получить доступ к исходному объекту, который определен. - person yeputons; 21.10.2020
comment
@yeputons - вот причина того, почему reinterpret_cast'ing struct_a*->void*->struct_a* четко определен. С другой стороны, struct_a*->void*->struct_b* и прямой atruct_a->struct_b* - нет. - person jaskmar; 22.10.2020
comment
@jaskmar Да, struct_a* -> struct_b* -> struct_a* - вот что происходит в примере, не так ли? VendorGlobalUserData - указатель. Это разрешено и эквивалентно struct_a* -> void* -> struct_b* -> void* -> struct_a*, см. Пункт 5 здесь. - person yeputons; 23.10.2020

Краткий ответ: если вы не знаете, что означает reinterpret_cast, не используйте его. Если вам это понадобится в будущем, вы узнаете.

Полный ответ:

Рассмотрим основные типы чисел.

Когда вы конвертируете, например, int(12) в unsigned float (12.0f), вашему процессору необходимо вызвать некоторые вычисления, поскольку оба числа имеют разное битовое представление. Это то, что означает static_cast.

С другой стороны, когда вы вызываете reinterpret_cast, ЦП не выполняет никаких вычислений. Он просто обрабатывает набор бит в памяти, как если бы он был другого типа. Поэтому, когда вы конвертируете int* в float* с помощью этого ключевого слова, новое значение (после разыменования указателя) не имеет ничего общего со старым значением в математическом смысле.

Пример:. Это правда, что reinterpret_cast не переносится по одной причине - порядок байтов (порядок байтов). Но на удивление часто это лучшая причина для его использования. Представьте себе пример: вам нужно прочитать двоичное 32-битное число из файла, и вы знаете, что оно имеет обратный порядок байтов. Ваш код должен быть универсальным и правильно работать в системах с прямым порядком байтов (например, некоторые ARM) и с прямым порядком байтов (например, x86). Итак, вам нужно проверить порядок байтов. Это хорошо известно во время компиляции, поэтому вы можете написать constexpr function: Для этого вы можете написать функцию:

/*constexpr*/ bool is_little_endian() {
  std::uint16_t x=0x0001;
  auto p = reinterpret_cast<std::uint8_t*>(&x);
  return *p != 0;
}

Пояснение: двоичное представление x в памяти может быть 0000'0000'0000'0001 (большой) или 0000'0001'0000'0000 (прямой порядок байтов). После переинтерпретации байт под указателем p может быть соответственно 0000'0000 или 0000'0001. Если вы используете статическое приведение, оно всегда будет 0000'0001, независимо от того, какой порядок байтов используется.

РЕДАКТИРОВАТЬ:

В первой версии я сделал пример функции is_little_endian constexpr. Он отлично компилируется на новейшем gcc (8.3.0), но стандарт говорит, что это незаконно. Компилятор clang отказывается его компилировать (что правильно).

person jaskmar    schedule 07.04.2017
comment
Хороший пример! Я бы заменил сокращение для uint16_t и unsigned char на uint8_t, чтобы сделать его менее непонятным для человека. - person Jan Turoň; 27.08.2018
comment
@ JanTuroň правда, нельзя предположить, что short занимает 16 бит в памяти. Исправленный. - person jaskmar; 28.08.2018
comment
Пример неверный. reinterpret_cast не допускается в функциях constexpr - person Michael Veksler; 02.03.2019
comment
Прежде всего, этот код отвергается как последней версией clang (7.0.0), так и gcc (8.2.0). К сожалению, я не нашел ограничения на формальном языке. Все, что я смог найти, это social.msdn.microsoft.com/Forums/vstudio/en-US/ - person Michael Veksler; 02.03.2019
comment
Вот лучшая ссылка (со ссылками на стандарт): stackoverflow.com/a/26201529/4955498 - person Michael Veksler; 03.03.2019
comment
В частности, en.cppreference.com/w/cpp/language/constant_expression (пункт 16) четко указывает, что reinterpret_cast нельзя использовать в постоянном выражении. Также посмотрите github.com/cplusplus/draft/blob/master/papers /N3797.pdf (5.19 постоянных выражений) pages125-126, что явно исключает reinterpret_cast. Затем 7.1.5 Спецификатор constexpr, элемент 5 (стр. 146) * Для нешаблонной, нестандартной функции constexpr ... если не существует значений аргументов, таких, что ... может быть оцененным подвыражением выражения основной константы (5.19), программа имеет неправильный формат * - person Michael Veksler; 03.03.2019
comment
Нет причин проверять порядок байтов во время выполнения. Ваш пример может заставить кого-то подумать, что это действительно хорошая идея. - person Daniel Kamil Kozar; 05.10.2019
comment
@DanielKamilKozar объясните свое заявление. Для этой цели в C ++ 20 даже появилась новая функция: en.cppreference.com / w / cpp / types / endian - person jaskmar; 12.12.2019
comment
@jaskmar ЦП просто не может переключить порядок байтов при выполнении кода. Что ж, технически это так, но это никому не принесет никакой пользы: что происходит со всеми данными и кодом в ОЗУ? Их порядок следования байтов остается прежним и не меняется волшебным образом. У процессоров с обратным порядком байтов порядок байтов устанавливается заранее с помощью кода инициализации, и выполнение фактического кода возможно только в том случае, если порядок байтов двоичного кода равен порядку байтов ЦП. Прочтите оригинал предложение - оно многое объясняет. - person Daniel Kamil Kozar; 13.12.2019
comment
@DanielKamilKozar кто говорит про переключение? Просто иногда нам нужна эта (только для чтения) информация. Особенно, если мы компилируем один код для множества разных архитектур. - person jaskmar; 13.12.2019
comment
Обратите внимание, что, хотя приведение чего-либо к char или (u) int8_t разрешено, приведение наоборот - UB (например, приведение uint8_t[2] к uint16_t). - person Mikhail Vasilyev; 09.06.2020
comment
@jaskmar Что касается вашей части объяснения, как это возможно, что результат static_cast будет давать одинаковые результаты независимо от порядка байтов? - person ST Renegade; 29.07.2020
comment
@STRenegade +1, я буду удивлен, что static_cast даст другой результат, чем пример reinterpret_cast в ответе. Я думаю, что переход от int* к float* тоже не покажет никаких различий. Мне нравится решение о том, как проверять порядок байтов во время выполнения, но я думаю, что объяснение reinterpret_cast вводит в заблуждение. - person Jeff; 25.11.2020

Значение reinterpret_cast не определяется стандартом C ++. Следовательно, теоретически reinterpret_cast может привести к сбою вашей программы. На практике компиляторы пытаются делать то, что вы ожидаете, а именно интерпретировать биты того, что вы передаете, как если бы они были типом, к которому вы приводите. Если вы знаете, что компиляторы, которые вы собираетесь использовать, делают с reinterpret_cast, вы можете использовать его, но сказать, что он переносимый, было бы ложью.

Для случая, который вы описываете, и практически для любого случая, когда вы могли бы рассмотреть reinterpret_cast, вы можете вместо этого использовать static_cast или другую альтернативу. Среди прочего, в стандарте говорится о том, чего можно ожидать от static_cast (§5.2.9):

Значение типа «указатель на cv void» может быть явно преобразовано в указатель на тип объекта. Значение указателя типа на объект, преобразованное в «указатель на cv void» и возвращающееся к исходному типу указателя, будет иметь свое исходное значение.

Таким образом, для вашего варианта использования кажется довольно очевидным, что комитет по стандартизации предназначал для вас использование static_cast.

person flodin    schedule 21.02.2009
comment
Не совсем сбой вашей программы. Стандарт предлагает несколько гарантий относительно reinterpret_cast. Но не так много, как люди часто ожидают. - person jalf; 21.02.2009
comment
Что ж, само reinterpret_cast, вероятно, не выйдет из строя, но он может вернуть какой-то фальшивый результат, который, когда вы попытаетесь его использовать, может вызвать сбой. - person flodin; 21.02.2009
comment
Нет, если вы используете его правильно. То есть reinterpret_cast от A до B к A совершенно безопасен и четко определен. Но значение B не указано, и да, если вы полагаетесь на это, могут случиться плохие вещи. Но сам слепок достаточно безопасен, если вы использовали его только так, как это позволяет стандарт. ;) - person jalf; 21.02.2009
comment
reinterpret_crash ‹› НЕ приведет к сбою вашего кода. Кроме этого мало гарантий. - person Martin York; 22.02.2009
comment
lol, я подозреваю, что reinterpret_crash действительно может привести к сбою вашей программы. Но reinterpret_cast не будет. ;) - person jalf; 22.02.2009
comment
‹Irony› Я попробовал это на своем компиляторе, и почему-то он отказался компилировать reinterpret_crash. Ошибка компилятора никоим образом не остановит меня от сбоя моей программы переинтерпретации. Я сообщу об ошибке как можно скорее! ‹/Irony› - person paercebal; 11.07.2010
comment
@paercebal template<class T, U> T reinterpret_crash(U a) { return *(T*)nullptr; } - person ; 14.01.2012

Один из вариантов использования reinterpret_cast - применить побитовые операции к поплавкам (IEEE 754). Одним из примеров этого был трюк с быстрым обратным квадратным корнем:

https://en.wikipedia.org/wiki/Fast_inverse_square_root#Overview_of_the_code

Он обрабатывает двоичное представление числа с плавающей запятой как целое число, сдвигает его вправо и вычитает его из константы, тем самым уменьшая вдвое и отрицая показатель степени. После преобразования обратно в число с плавающей запятой он подвергается итерации Ньютона-Рафсона, чтобы сделать это приближение более точным:

float Q_rsqrt( float number )
{
    long i;
    float x2, y;
    const float threehalfs = 1.5F;

    x2 = number * 0.5F;
    y  = number;
    i  = * ( long * ) &y;                       // evil floating point bit level hacking
    i  = 0x5f3759df - ( i >> 1 );               // what the deuce? 
    y  = * ( float * ) &i;
    y  = y * ( threehalfs - ( x2 * y * y ) );   // 1st iteration
//  y  = y * ( threehalfs - ( x2 * y * y ) );   // 2nd iteration, this can be removed

    return y;
}

Первоначально он был написан на C, поэтому использует приведение типов C, но аналогичное приведение C ++ - это reinterpret_cast.

person Adam P. Goucher    schedule 12.01.2016
comment
error: invalid cast of an rvalue expression of type 'int64_t {aka long long int}' to type 'double&' reinterpret_cast<double&>((reinterpret_cast<int64_t&>(d) >> 1) + (1L << 61)) - ideone.com/6S4ijc - person Orwellophile; 10.07.2016
comment
В стандарте указано, что это неопределенное поведение: en.cppreference.com/w/cpp/language / reinterpret_cast (под псевдонимом типа) - person Cris Luengo; 03.03.2017
comment
@CrisLuengo Если я заменю все reinterpret_cast на memcpy, это все равно UB? - person sandthorn; 16.07.2018
comment
@sandthorn: это UB в соответствии со стандартом, но если он работает для вашей архитектуры, не беспокойтесь об этом. Полагаю, этот трюк подходит для любого компилятора для архитектур Intel. Он не мог работать должным образом (или даже давать сбой) на других архитектурах - например, возможно, что числа с плавающей запятой и длинные числа хранятся в отдельных отсеках памяти (не то, чтобы я знал о какой-либо такой архитектуре, это просто аргумент ...) . memcpy определенно сделает это законным. - person Cris Luengo; 16.07.2018

Вы можете использовать reinterprete_cast для проверки наследования во время компиляции.
Посмотрите здесь: Использование reinterpret_cast для проверки наследования во время компиляции

person Martin R.    schedule 29.10.2014

Вот вариант программы Ави Гинзбурга, который ясно иллюстрирует свойство reinterpret_cast, упомянутое Крисом Луенго, flodin и cmdLP: компилятор обрабатывает указанную ячейку памяти, как если бы это был объект нового типа:

#include <iostream>
#include <string>
#include <iomanip>
using namespace std;

class A
{
public:
    int i;
};

class B : public A
{
public:
    virtual void f() {}
};

int main()
{
    string s;
    B b;
    b.i = 0;
    A* as = static_cast<A*>(&b);
    A* ar = reinterpret_cast<A*>(&b);
    B* c = reinterpret_cast<B*>(ar);
    
    cout << "as->i = " << hex << setfill('0')  << as->i << "\n";
    cout << "ar->i = " << ar->i << "\n";
    cout << "b.i   = " << b.i << "\n";
    cout << "c->i  = " << c->i << "\n";
    cout << "\n";
    cout << "&(as->i) = " << &(as->i) << "\n";
    cout << "&(ar->i) = " << &(ar->i) << "\n";
    cout << "&(b.i) = " << &(b.i) << "\n";
    cout << "&(c->i) = " << &(c->i) << "\n";
    cout << "\n";
    cout << "&b = " << &b << "\n";
    cout << "as = " << as << "\n";
    cout << "ar = " << ar << "\n";
    cout << "c  = " << c  << "\n";
    
    cout << "Press ENTER to exit.\n";
    getline(cin,s);
}

В результате получается такой вывод:

as->i = 0
ar->i = 50ee64
b.i   = 0
c->i  = 0

&(as->i) = 00EFF978
&(ar->i) = 00EFF974
&(b.i)   = 00EFF978
&(c->i)  = 00EFF978

&b = 00EFF974
as = 00EFF978
ar = 00EFF974
c  = 00EFF974
Press ENTER to exit.

Можно видеть, что объект B встраивается в память сначала как данные, специфичные для B, а затем - внедренный объект A. static_cast правильно возвращает адрес встроенного объекта A, а указатель, созданный static_cast, правильно дает значение поля данных. Указатель, сгенерированный reinterpret_cast, обрабатывает область памяти b, как если бы это был простой объект A, и поэтому, когда указатель пытается получить поле данных, он возвращает некоторые данные, специфичные для B, как если бы это было содержимое этого поля.

Одно из применений reinterpret_cast - преобразование указателя в целое число без знака (когда указатели и целые числа без знака имеют одинаковый размер):

int i; unsigned int u = reinterpret_cast<unsigned int>(&i);

person TRPh    schedule 23.10.2019
comment
Здесь все, кроме последнего примера, относится к неопределенному поведению; это интересно только как (ненадежное) средство иллюстрации деталей реализации языка. - person Davis Herring; 18.08.2020

template <class outType, class inType>
outType safe_cast(inType pointer)
{
    void* temp = static_cast<void*>(pointer);
    return static_cast<outType>(temp);
}

Я попытался подвести итог и написал простой безопасный слепок с использованием шаблонов. Обратите внимание, что это решение не гарантирует приведения указателей к функциям.

person Саша Зезюлинский    schedule 18.01.2013
comment
Какие? Зачем беспокоиться? Именно это reinterpret_cast уже делает в этой ситуации: указатель объекта может быть явно преобразован в указатель объекта другого типа. [72] Когда prvalue v типа указателя объекта преобразуется в тип указателя объекта «указатель на cv T», результатом будет static_cast<cv T*>(static_cast<cv void*>(v)). - N3797. - person underscore_d; 07.01.2016
comment
Что касается c++2003 стандарта, я НЕ обнаружил, что reinterpret_cast выполняет static_cast<cv T*>(static_cast<cv void*>(v)) - person Саша Зезюлинский; 12.01.2016
comment
Хорошо, правда, но меня не волнует версия, выпущенная 13 лет назад, и большинство кодеров не должны этого делать, если (что вполне вероятно) они могут ее избежать. Ответы и комментарии должны действительно отражать последний доступный Стандарт, если не указано иное ... ИМХО. В любом случае, я предполагаю, что Комитет посчитал необходимым добавить это явно после 2003 года (поскольку IIRC, это было то же самое в C ++ 11). - person underscore_d; 16.01.2016
comment
До C++03 было C++98. Тонны проектов использовали старый C ++ вместо переносимого C. Иногда нужно заботиться о переносимости. Например, вы должны поддерживать один и тот же код в Solaris, AIX, HPUX, Windows. Когда дело доходит до зависимости и переносимости компилятора, это сложно. Итак, хороший пример внедрения ада переносимости - использование reinterpret_cast в вашем коде - person Саша Зезюлинский; 17.01.2016
comment
опять же, если, как и я, вы счастливы ограничить себя только платформами, которые хорошо сочетаются с последней и лучшей версией языка, ваше возражение - спорный вопрос. - person underscore_d; 17.01.2016
comment
Ваша функция safe_cast делает то же самое, что и reinterpret_cast: stackoverflow.com/a/68137312/5447906. - person anton_rh; 26.06.2021

Сначала у вас есть данные определенного типа, например int:

int x = 0x7fffffff://==nan in binary representation

Затем вы хотите получить доступ к той же переменной, что и другой тип, например float: вы можете выбрать между

float y = reinterpret_cast<float&>(x);

//this could only be used in cpp, looks like a function with template-parameters

or

float y = *(float*)&(x);

//this could be used in c and cpp

КРАТКО: это означает, что одна и та же память используется как другой тип. Таким образом, вы можете преобразовать двоичные представления чисел с плавающей запятой в виде типа int, как указано выше, в числа с плавающей запятой. Например, 0x80000000 равен -0 (мантисса и показатель степени равны нулю, но знак msb равен единице. Это также работает для удвоений и длинных удвоений.

ОПТИМИЗАЦИЯ: я думаю, что reinterpret_cast будет оптимизирован во многих компиляторах, в то время как c-приведение выполняется с помощью указателей (значение должно быть скопировано в память, потому что указатели не могут указывать на регистры процессора).

ПРИМЕЧАНИЕ: В обоих случаях вы должны сохранить приведенное значение в переменной перед приведением! Этот макрос может помочь:

#define asvar(x) ({decltype(x) __tmp__ = (x); __tmp__; })
person cmdLP    schedule 23.02.2017
comment
Верно, что это означает, что одна и та же память используется в качестве другого типа, но ограничена определенной парой типов. В вашем примере reinterpret_cast форма от int до float& - это неопределенное поведение. - person jaskmar; 19.12.2019
comment
Компиляторы оптимизируют memcpy только для регистрации операций, когда это возможно; преобразования просты (но они также являются UB - если значение used - как указано на всей этой странице). - person Davis Herring; 18.08.2020

Быстрый ответ: используйте static_cast, если он компилируется, в противном случае используйте reinterpret_cast.

person Marius K    schedule 10.11.2016

Прочтите FAQ! Хранение данных C ++ в C может быть рискованным.

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

person dirkgently    schedule 21.02.2009