Namespaces
Variants

reinterpret_cast conversion

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

Преобразует между типами путем переинтерпретации базового битового шаблона.

Содержание

Синтаксис

reinterpret_cast< тип-цели >( выражение )

Возвращает значение типа target-type .

Объяснение

В отличие от static_cast , но подобно const_cast , выражение reinterpret_cast не компилируется в какие-либо инструкции процессора (за исключением преобразований между целыми числами и указателями, или между указателями на экзотических архитектурах, где представление указателя зависит от его типа). Это в основном директива времени компиляции, которая предписывает компилятору рассматривать expression так, как если бы оно имело тип target-type .

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

1) Выражение целочисленного типа, перечисления, указателя или указателя на член класса может быть преобразовано в свой собственный тип. Результирующее значение совпадает со значением expression .
2) Указатель может быть преобразован в любой целочисленный тип, достаточно большой для хранения всех значений своего типа (например, в std::uintptr_t ).
3) Значение любого целочисленного или перечислимого типа может быть преобразовано в указатель. Указатель, преобразованный в целое число достаточного размера и обратно в тот же тип указателя, гарантированно сохраняет исходное значение, в противном случае результирующий указатель не может быть безопасно разыменован (обратное преобразование в противоположном направлении не гарантируется; один и тот же указатель может иметь несколько целочисленных представлений). Константа нулевого указателя NULL или целочисленный ноль не гарантируют получения значения нулевого указателя целевого типа; static_cast или неявное преобразование должны использоваться для этой цели.
4) Любое значение типа std::nullptr_t , включая nullptr , может быть преобразовано в любой целочисленный тип, как если бы это было ( void * ) 0 , но никакое значение, даже nullptr , не может быть преобразовано в std::nullptr_t : для этой цели следует использовать static_cast .
(начиная с C++11)
5) Любой указатель на объектный тип T1* может быть преобразован в другой указатель на объектный тип cv T2* . Это эквивалентно static_cast < cv T2 * > ( static_cast < cv void * > ( expression ) ) (что подразумевает, что если требование выравнивания T2 не строже, чем у T1 , значение указателя не изменяется и преобразование результирующего указателя обратно в исходный тип дает исходное значение). В любом случае, результирующий указатель может быть безопасно разыменован только если разыменовываемое значение доступно по типу .
6) Выражение lvalue (до C++11) glvalue (начиная с C++11) типа T1 может быть преобразовано в ссылку на другой тип T2 . Результат эквивалентен * reinterpret_cast < T2 * > ( p ) , где p — указатель типа «указатель на T1 » на объект или функцию, обозначенные выражением . Временный объект не материализуется и не (начиная с C++17) создаётся, копирование не выполняется, конструкторы и преобразующие функции не вызываются. Результирующая ссылка может быть безопасно использована только если она доступна по типу .
7) Любой указатель на функцию может быть преобразован в указатель на другой тип функции. Результат не определён, но преобразование такого указателя обратно в указатель на исходный тип функции даёт указатель на исходную функцию. Полученный указатель может быть безопасно вызван только если его тип функции совместим по вызову с исходным типом функции.
8) В некоторых реализациях (в частности, на любой POSIX-совместимой системе, как того требует dlsym ), указатель на функцию может быть преобразован в void * или любой другой указатель на объект, и наоборот. Если реализация поддерживает преобразование в обоих направлениях, преобразование к исходному типу даёт исходное значение, в противном случае результирующий указатель не может быть безопасно разыменован или вызван.
9) Нулевое значение указателя любого типа может быть преобразовано в указатель любого другого типа, в результате чего получается нулевое значение указателя этого типа. Обратите внимание, что нулевой указатель-константа nullptr или любое другое значение типа std::nullptr_t не может быть преобразовано в указатель с помощью reinterpret_cast : для этой цели следует использовать неявное преобразование или static_cast .
10) Указатель на член-функцию может быть преобразован в указатель на другую член-функцию другого типа. Преобразование обратно в исходный тип дает исходное значение, в противном случае результирующий указатель не может быть безопасно использован.
11) Указатель на член-объект некоторого класса T1 может быть преобразован в указатель на другой член-объект другого класса T2 . Если выравнивание T2 не строже, чем у T1 , преобразование обратно к исходному типу T1 даёт исходное значение, иначе результирующий указатель не может быть безопасно использован.

Как и для всех выражений приведения, результат:

  • lvalue, если target-type является типом lvalue-ссылки или rvalue-ссылкой на тип функции (начиная с C++11) ;
  • xvalue, если target-type является rvalue-ссылкой на объектный тип;
(since C++11)
  • в противном случае является prvalue.

Псевдонимы типов

Доступность типа

Если тип T_ref является подобным любому из следующих типов, объект динамического типа T_obj является тип-доступным через lvalue (до C++11) glvalue (начиная с C++11) типа T_ref :

  • char , unsigned char или std::byte (начиная с C++17) : это позволяет исследовать представление объекта любого объекта как массив байтов.
  • T_obj
  • соответствующий знаковый или беззнаковый тип для T_obj

Если программа пытается прочитать или изменить сохранённое значение объекта через lvalue (until C++11) glvalue (since C++11) через которое оно не является типодоступным, поведение не определено.

Это правило включает анализ псевдонимов на основе типов, при котором компилятор предполагает, что значение, прочитанное через glvalue одного типа, не изменяется записью в glvalue другого типа (с учетом исключений, указанных выше).

Обратите внимание, что многие компиляторы C++ ослабляют это правило в качестве нестандартного расширения языка, разрешая доступ через неактивный член union с неправильным типом (такой доступ не является неопределённым поведением в C).

Совместимость вызовов

Если выполняется любое из следующих условий, тип T_call является call-совместимым с функциональным типом T_func :

  • T_call имеет тот же тип, что и T_func .
(начиная с C++17)

Если функция вызывается через выражение, чей тип функции не является совместимым по вызову с типом определения вызываемой функции, поведение не определено.

Примечания

При условии соблюдения требований выравнивания, reinterpret_cast не изменяет значение указателя за исключением нескольких ограниченных случаев, связанных с pointer-interconvertible объектами:

struct S1 { int a; } s1;
struct S2 { int a; private: int b; } s2; // не стандартная компоновка
union U { int a; double b; } u = {0};
int arr[2];
int* p1 = reinterpret_cast<int*>(&s1); // значение p1 - "указатель на s1.a", потому что
                                       // s1.a и s1 являются взаимопреобразуемыми по указателю
int* p2 = reinterpret_cast<int*>(&s2); // значение p2 не изменяется reinterpret_cast
                                       // и является "указателем на s2"
int* p3 = reinterpret_cast<int*>(&u);  // значение p3 - "указатель на u.a":
                                       // u.a и u являются взаимопреобразуемыми по указателю
double* p4 = reinterpret_cast<double*>(p3); // значение p4 - "указатель на u.b": u.a и
                                            // u.b являются взаимопреобразуемыми по указателю, потому что
                                            // оба взаимопреобразуемы по указателю с u
int* p5 = reinterpret_cast<int*>(&arr); // значение p5 не изменяется reinterpret_cast
                                        // и является "указателем на arr"

Выполнение доступа к члену класса, который обозначает нестатическое поле данных или нестатическую функцию-член, для glvalue, которое фактически не обозначает объект соответствующего типа - например, полученного с помощью reinterpret_cast - приводит к неопределённому поведению:

struct S { int x; };
struct T { int x; int f(); };
struct S1 : S {};    // стандартная компоновка
struct ST : S, T {}; // не стандартная компоновка
S s = {};
auto p = reinterpret_cast<T*>(&s); // значение p - "указатель на s"
auto i = p->x; // доступ к члену класса - неопределенное поведение;
               // s не является объектом типа T
p->x = 1; // неопределенное поведение
p->f();   // неопределенное поведение
S1 s1 = {};
auto p1 = reinterpret_cast<S*>(&s1); // значение p1 - "указатель на подобъект S объекта s1"
auto i = p1->x; // OK
p1->x = 1;      // OK
ST st = {};
auto p2 = reinterpret_cast<S*>(&st); // значение p2 - "указатель на st"
auto i = p2->x; // неопределенное поведение
p2->x = 1;      // неопределенное поведение

Многие компиляторы выдают предупреждения о "strict aliasing" в таких случаях, даже если технически такие конструкции нарушают нечто иное, чем параграф, обычно известный как "strict aliasing rule".

Цель строгого алиасинга и связанных правил заключается в том, чтобы включить анализ алиасов на основе типов, который был бы разрушен, если бы программа могла корректно создать ситуацию, в которой два указателя на несвязанные типы (например, int * и float * ) могли одновременно существовать и оба могли использоваться для загрузки или сохранения одной и той же памяти (см. это письмо на SG12 reflector ). Таким образом, любая техника, которая, казалось бы, способна создать такую ситуацию, обязательно вызывает неопределённое поведение.

Когда необходимо интерпретировать байты объекта как значение другого типа, std::memcpy или std::bit_cast (since C++20) может быть использован:

double d = 0.1;
std::int64_t n;
static_assert(sizeof n == sizeof d);
// n = *reinterpret_cast<std::int64_t*>(&d); // Неопределённое поведение
std::memcpy(&n, &d, sizeof d);               // OK
n = std::bit_cast<std::int64_t>(d);          // также OK

Если реализация предоставляет std::intptr_t и/или std::uintptr_t , то приведение указателя на тип объекта или cv void к этим типам всегда корректно определено. Однако это не гарантируется для указателей на функции.

(начиная с C++11)

В C, агрегатное копирование и присваивание обращаются к агрегатному объекту как к целому. Но в C++ такие действия всегда выполняются через вызов функции-члена, которая обращается к отдельным подобъектам, а не ко всему объекту (или, в случае объединений, копирует представление объекта, т.е. через unsigned char ).

Ключевые слова

reinterpret_cast

Пример

Демонстрирует некоторые применения reinterpret_cast :

#include <cassert>
#include <cstdint>
#include <iostream>
int f() { return 42; }
int main()
{
    int i = 7;
    // pointer to integer and back
    std::uintptr_t v1 = reinterpret_cast<std::uintptr_t>(&i); // static_cast is an error
    std::cout << "The value of &i is " << std::showbase << std::hex << v1 << '\n';
    int* p1 = reinterpret_cast<int*>(v1);
    assert(p1 == &i);
    // pointer to function to another and back
    void(*fp1)() = reinterpret_cast<void(*)()>(f);
    // fp1(); undefined behavior
    int(*fp2)() = reinterpret_cast<int(*)()>(fp1);
    std::cout << std::dec << fp2() << '\n'; // safe
    // type aliasing through pointer
    char* p2 = reinterpret_cast<char*>(&i);
    std::cout << (p2[0] == '\x7' ? "This system is little-endian\n"
                                 : "This system is big-endian\n");
    // type aliasing through reference
    reinterpret_cast<unsigned int&>(i) = 42;
    std::cout << i << '\n';
    [[maybe_unused]] const int &const_iref = i;
    // int &iref = reinterpret_cast<int&>(
    //     const_iref); // compiler error - can't get rid of const
    // Must use const_cast instead: int &iref = const_cast<int&>(const_iref);
}

Возможный вывод:

The value of &i is 0x7fff352c3580
42
This system is little-endian
42

Отчёты о дефектах

Следующие отчеты об изменениях поведения, влияющие на дефекты, были применены задним числом к ранее опубликованным стандартам C++.

DR Применяется к Поведение в опубликованной версии Корректное поведение
CWG 195 C++98 преобразование между указателями на функции
и указателями на объекты не разрешалось
сделано условно-поддерживаемым
CWG 658 C++98 результат преобразований указателей был неопределен
(кроме преобразований обратно к исходному типу)
предоставлена спецификация для указателей
с типами, на которые они указывают, удовлетворяющими
требованиям выравнивания
CWG 799 C++98 было неясно, какое тождественное преобразование
может быть выполнено с помощью reinterpret_cast
прояснено
CWG 1268 C++11 reinterpret_cast мог приводить только
lvalues к ссылочным типам
xvalues также разрешены
CWG 2780 C++98 reinterpret_cast не мог приводить
lvalues функций к другим ссылочным типам
разрешено
CWG 2939 C++17 reinterpret_cast мог приводить
prvalues к типам rvalue-ссылок
не разрешено

Ссылки

  • Стандарт C++23 (ISO/IEC 14882:2024):
  • 7.6.1.10 Реинтерпретирующее приведение [expr.reinterpret.cast]
  • Стандарт C++20 (ISO/IEC 14882:2020):
  • 7.6.1.9 Reinterpret cast [expr.reinterpret.cast]
  • Стандарт C++17 (ISO/IEC 14882:2017):
  • 8.2.10 Reinterpret cast [expr.reinterpret.cast]
  • Стандарт C++14 (ISO/IEC 14882:2014):
  • 5.2.10 Reinterpret cast [expr.reinterpret.cast]
  • Стандарт C++11 (ISO/IEC 14882:2011):
  • 5.2.10 Reinterpret cast [expr.reinterpret.cast]
  • Стандарт C++98 (ISO/IEC 14882:1998):
  • 5.2.10 Reinterpret cast [expr.reinterpret.cast]
  • Стандарт C++03 (ISO/IEC 14882:2003):
  • 5.2.10 Reinterpret cast [expr.reinterpret.cast]

Смотрите также

const_cast conversion добавляет или удаляет const
static_cast conversion выполняет базовые преобразования
dynamic_cast conversion выполняет проверенные полиморфные преобразования
явные приведения разрешающие преобразования между типами
стандартные преобразования неявные преобразования из одного типа в другой
(C++20)
переинтерпретирует представление объекта одного типа как представление другого типа
(шаблон функции)