reinterpret_cast преобразование
Преобразует значения между типами, переинтерпретируя базовый битовый шаблон.
Синтаксис
reinterpret_cast< целевой-тип >( выражение ) |
Возвращает значение типа целевой-тип.
Пояснение
В отличие от static_cast, но подобно const_cast, выражение reinterpret_cast не компилируется в инструкции ЦП (за исключением преобразования между целыми числами и указателями или между указателями на редких архитектурах, где представление указателя зависит от его типа). В первую очередь это директива времени компиляции, которая указывает компилятору трактовать выражение так, как если бы оно имело тип целевой-тип.
С помощью reinterpret_cast можно выполнить только следующие преобразования, за исключением случаев, когда такие преобразования снимают константность (или volatile-квалификатор).
std::uintptr_t).NULL или целочисленный ноль не гарантированно дают нулевое значение указателя целевого типа; для этой цели следует использовать static_cast или неявное преобразование. 4) Любое значение типа std::nullptr_t, включая nullptr, можно преобразовать в любой целочисленный тип так, как если бы оно было (void*)0, но никакое значение, даже nullptr, нельзя преобразовать в std::nullptr_t: для этой цели следует использовать static_cast. | (начиная с C++11) |
T1* можно преобразовать в другой тип указателя на объект cv T2*. Это полностью эквивалентно static_cast<cvT2*>(static_cast<cvvoid*>(expression)) (из чего следует, что если требование к выравниванию T2 не строже требования T1, значение указателя не меняется, а преобразование полученного указателя обратно в исходный тип возвращает исходное значение). В любом случае полученный указатель можно безопасно разыменовывать, только если разыменованное значение доступно через данный тип.T1 можно преобразовать в ссылку на другой тип T2. Результат соответствует *reinterpret_cast<T2*>(p), где p — указатель типа «указатель на T1» на объект или функцию, обозначаемую выражением. Временный объект не материализуется и не(начиная с C++17) создаётся, копирование не выполняется, конструкторы и функции преобразования не вызываются. Полученную ссылку можно безопасно использовать, только если соответствующий объект доступен через данный тип.dlsym) указатель на функцию можно преобразовать в void* или любой другой указатель на объект и наоборот. Если реализация поддерживает преобразование в обоих направлениях, преобразование к исходному типу возвращает исходное значение; в противном случае полученный указатель нельзя безопасно разыменовывать или вызывать.nullptr или любое другое значение типа std::nullptr_t нельзя преобразовать в указатель с помощью reinterpret_cast: для этой цели следует использовать неявное преобразование или static_cast.T1 можно преобразовать в указатель на другой объект-член другого класса T2. Если выравнивание T2 не строже выравнивания T1, преобразование обратно в исходный тип T1 возвращает исходное значение; в противном случае полученный указатель нельзя безопасно использовать.Как и для всех выражений преобразования, результат является:
- lvalue, если целевой-тип — тип ссылки на lvalue или rvalue-ссылка на тип функции(начиная с C++11);
| (начиная с C++11) |
- prvalue в остальных случаях.
Псевдонимы типов
Доступность через тип
Если тип T_ref подобен любому из следующих типов, объект динамического типа T_obj является доступным через данный тип посредством lvalue(до C++11)glvalue(начиная с C++11) типа T_ref:
-
char, unsignedchar или
std::byte(начиная с C++17): это позволяет исследовать представление объекта любого объекта как массив байтов. T_obj- соответствующий
T_objзнаковый или беззнаковый тип
Если программа пытается прочитать или изменить сохранённое значение объекта посредством lvalue(до C++11)glvalue(начиная с C++11), через которое этот объект недоступен, поведение не определено.
Это правило позволяет выполнять анализ псевдонимов на основе типов: компилятор предполагает, что значение, прочитанное через glvalue одного типа, не изменяется записью через glvalue другого типа (с учётом указанных выше исключений).
Обратите внимание, что многие компиляторы ослабляют это правило как нестандартное расширение языка, разрешая доступ к члену неправильного типа через неактивный член объединения (в C такой доступ не приводит к неопределённому поведению).
Совместимость для вызова
Тип T_call совместим для вызова с типом функции T_func, если выполняется хотя бы одно из следующих условий:
-
T_callимеет тот же тип, что иT_func.
| (начиная с C++17) |
Если функция вызывается через выражение, тип функции которого несовместим для вызова с типом определения вызываемой функции, поведение не определено.
Примечания
При соблюдении требований к выравниванию reinterpret_cast не изменяет значение указателя, за исключением нескольких ограниченных случаев, связанных с объектами, взаимно преобразуемыми с помощью указателя:
struct S1 { int a; } s1;
struct S2 { int a; private: int b; } s2; // not standard-layout
union U { int a; double b; } u = {0};
int arr[2];
int* p1 = reinterpret_cast<int*>(&s1); // value of p1 is "pointer to s1.a" because
// s1.a and s1 are pointer-interconvertible
int* p2 = reinterpret_cast<int*>(&s2); // value of p2 is unchanged by reinterpret_cast
// and is "pointer to s2".
int* p3 = reinterpret_cast<int*>(&u); // value of p3 is "pointer to u.a":
// u.a and u are pointer-interconvertible
double* p4 = reinterpret_cast<double*>(p3); // value of p4 is "pointer to u.b": u.a and
// u.b are pointer-interconvertible because
// both are pointer-interconvertible with u
int* p5 = reinterpret_cast<int*>(&arr); // value of p5 is unchanged by reinterpret_cast
// and is "pointer to arr"
Доступ к члену класса, обозначающий нестатический член-данные или нестатическую функцию-член, через glvalue, которое фактически не обозначает объект соответствующего типа (например, полученное с помощью reinterpret_cast), приводит к неопределённому поведению:
struct S { int x; };
struct T { int x; int f(); };
struct S1 : S {}; // standard-layout
struct ST : S, T {}; // not standard-layout
S s = {};
auto p = reinterpret_cast<T*>(&s); // value of p is "pointer to s"
auto i = p->x; // class member access expression is undefined behavior;
// s is not a T object
p->x = 1; // undefined behavior
p->f(); // undefined behavior
S1 s1 = {};
auto p1 = reinterpret_cast<S*>(&s1); // value of p1 is "pointer to the S subobject of s1"
auto i = p1->x; // OK
p1->x = 1; // OK
ST st = {};
auto p2 = reinterpret_cast<S*>(&st); // value of p2 is "pointer to st"
auto i = p2->x; // undefined behavior
p2->x = 1; // undefined behavior
Во многих случаях компиляторы выдают предупреждения о «строгом алиасинге», хотя технически такие конструкции нарушают нечто иное, чем правило, обычно называемое «правилом строгого алиасинга».
Цель строгого алиасинга и связанных с ним правил — обеспечить анализ псевдонимов на основе типов. Этот анализ оказался бы невозможен, если бы программа могла корректно создать ситуацию, в которой одновременно существуют два указателя на несвязанные типы (например, int* и float*), и оба указателя можно использовать для чтения или записи в одну и ту же область памяти (см. это письмо в рассылке SG12). Поэтому любой метод, который, по-видимому, позволяет создать такую ситуацию, неизбежно приводит к неопределённому поведению.
Если требуется интерпретировать байты объекта как значение другого типа, можно использовать std::memcpy или std::bit_cast(начиная с C++20):
double d = 0.1; std::int64_t n; static_assert(sizeof n == sizeof d); // n = *reinterpret_cast<std::int64_t*>(&d); // Undefined behavior std::memcpy(&n, &d, sizeof d); // OK n = std::bit_cast<std::int64_t>(d); // also OK
| Если реализация предоставляет | (начиная с C++11) |
В C копирование и присваивание агрегата обращаются к объекту-агрегату целиком. В C++ же такие действия всегда выполняются посредством вызова функции-члена, которая обращается к отдельным подобъектам, а не ко всему объекту (или, в случае объединений, копирует представление объекта, то есть посредством unsignedchar).
Ключевые слова
Пример
Демонстрирует некоторые способы использования 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 мог преобразовывать только lvalue в ссылочные типы | разрешены также xvalue |
| CWG 2780 | C++98 |
reinterpret_cast не мог преобразовывать lvalue-функции в другие ссылочные типы | разрешено |
| CWG 2939 | C++17 |
reinterpret_cast мог преобразовывать prvalue в типы rvalue-ссылок | не разрешено |
Ссылки
- Стандарт C++23 (ISO/IEC 14882:2024):
- 7.6.1.10 Преобразование reinterpret [expr.reinterpret.cast]
- Стандарт C++20 (ISO/IEC 14882:2020):
- 7.6.1.9 Преобразование reinterpret [expr.reinterpret.cast]
- Стандарт C++17 (ISO/IEC 14882:2017):
- 8.2.10 Преобразование reinterpret [expr.reinterpret.cast]
- Стандарт C++14 (ISO/IEC 14882:2014):
- 5.2.10 Преобразование reinterpret [expr.reinterpret.cast]
- Стандарт C++11 (ISO/IEC 14882:2011):
- 5.2.10 Преобразование reinterpret [expr.reinterpret.cast]
- Стандарт C++98 (ISO/IEC 14882:1998):
- 5.2.10 Преобразование reinterpret [expr.reinterpret.cast]
- Стандарт C++03 (ISO/IEC 14882:2003):
- 5.2.10 Преобразование reinterpret [expr.reinterpret.cast]
См. также
const_cast преобразование | добавляет или удаляет const |
static_cast преобразование | выполняет базовые преобразования |
dynamic_cast преобразование | выполняет проверяемые полиморфные преобразования |
| явные преобразования | допускающие преобразования между типами |
| стандартные преобразования | неявные преобразования одного типа в другой |
|
(C++20) | переинтерпретирует представление объекта одного типа как представление другого (шаблон функции) |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/reinterpret_cast