reinterpret_cast
conversion
Преобразует между типами путем переинтерпретации базового битового шаблона.
Содержание |
Синтаксис
reinterpret_cast<
тип-цели
>(
выражение
)
|
|||||||||
Возвращает значение типа target-type .
Объяснение
В отличие от static_cast , но подобно const_cast , выражение reinterpret_cast не компилируется в какие-либо инструкции процессора (за исключением преобразований между целыми числами и указателями, или между указателями на экзотических архитектурах, где представление указателя зависит от его типа). Это в основном директива времени компиляции, которая предписывает компилятору рассматривать expression так, как если бы оно имело тип target-type .
Только следующие преобразования могут быть выполнены с помощью reinterpret_cast , за исключением случаев, когда такие преобразования отбрасывают константность (или волатильность).
static_cast
или
неявное преобразование
должны использоваться для этой цели.
|
4)
Любое значение типа
std::nullptr_t
, включая
nullptr
, может быть преобразовано в любой целочисленный тип, как если бы это было
(
void
*
)
0
, но никакое значение, даже
nullptr
, не может быть преобразовано в
std::nullptr_t
: для этой цели следует использовать
static_cast
.
|
(начиная с C++11) |
T1*
может быть преобразован в другой указатель на объектный тип
cv
T2*
. Это эквивалентно
static_cast
<
cv
T2
*
>
(
static_cast
<
cv
void
*
>
(
expression
)
)
(что подразумевает, что если требование выравнивания
T2
не строже, чем у
T1
, значение указателя не изменяется и преобразование результирующего указателя обратно в исходный тип дает исходное значение). В любом случае, результирующий указатель может быть безопасно разыменован только если разыменовываемое значение
доступно по типу
.
T1
может быть преобразовано в ссылку на другой тип
T2
. Результат эквивалентен
*
reinterpret_cast
<
T2
*
>
(
p
)
, где
p
— указатель типа «указатель на
T1
» на объект или функцию, обозначенные
выражением
. Временный объект не
материализуется и не
(начиная с C++17)
создаётся, копирование не выполняется, конструкторы и преобразующие функции не вызываются. Результирующая ссылка может быть безопасно использована только если она
доступна по типу
.
dlsym
), указатель на функцию может быть преобразован в
void
*
или любой другой указатель на объект, и наоборот. Если реализация поддерживает преобразование в обоих направлениях, преобразование к исходному типу даёт исходное значение, в противном случае результирующий указатель не может быть безопасно разыменован или вызван.
T1
может быть преобразован в указатель на другой член-объект другого класса
T2
. Если выравнивание
T2
не строже, чем у
T1
, преобразование обратно к исходному типу
T1
даёт исходное значение, иначе результирующий указатель не может быть безопасно использован.
Как и для всех выражений приведения, результат:
- lvalue, если target-type является типом lvalue-ссылки или rvalue-ссылкой на тип функции (начиная с C++11) ;
|
(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 :
#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)
|
переинтерпретирует представление объекта одного типа как представление другого типа
(шаблон функции) |