reinterpret_cast преобразование
Преобразует между типами, интерпретируя внутреннее битовое представление.
Синтаксис
reinterpret_cast< тип-цели >( выражение ) |
Возвращает значение типа тип-цели.
Объяснение
В отличие от static_cast, но как и const_cast, выражение reinterpret_cast не компилируется в какие-либо инструкции процессора (за исключением преобразований между целыми числами и указателями, или между указателями на необычных архитектурах, где представление указателя зависит от его типа). В основном это директива компилятора, которая инструктирует компилятор рассматривать выражение как имеющее тип тип-цели.
Только следующие преобразования могут быть выполнены с помощью reinterpret_cast, за исключением случаев, когда такие преобразования будут убирать const (или 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<cv T2*>(static_cast<cv void*>(expression)) (что подразумевает, что если требование выравнивания T2 не строже, чем у T1, значение указателя не изменяется, и преобразование полученного указателя обратно к его исходному типу даёт исходное значение). В любом случае, результирующий указатель может быть разыменован безопасно только если это разрешено правилами алиасирования типов (см. ниже).T1 может быть преобразовано в ссылку на другой тип T2. Результатом является результат *reinterpret_cast<T2*>(p), где p - указатель типа "указатель на T1" на объект, обозначаемый выражение. Временная переменная не создаётся, ни одна копия не делается, ни один конструктор или функция преобразования не вызываются. К результирующей ссылке можно обращаться безопасно только если это разрешено правилами алиасирования типов (см. ниже).dlsym), указатель на функцию может быть преобразован в void* или любой другой указатель объекта, или наоборот. Если реализация поддерживает преобразование в обоих направлениях, преобразование к исходному типу даёт исходное значение, в противном случае результирующий указатель не может быть разыменован или вызван безопасно.nullptr или любое другое значение типа std::nullptr_t не может быть преобразовано в указатель с помощью reinterpret_cast: для этой цели следует использовать неявное преобразование или static_cast.T1 может быть преобразован в указатель на другой член-объект другого класса T2. Если выравнивание T2 не строже, чем выравнивание T1, преобразование обратно к исходному типу T1 даёт исходное значение, в противном случае результирующий указатель не может быть использован безопасно.Как и при всех выражениях преобразования, результат:
- lvalue, если тип-цели — тип ссылки lvalue или ссылка rvalue на тип функции(с C++11);
| (с C++11) |
- prvalue в противном случае.
Ключевые слова
Алиясирование типов
Всякий раз, когда предпринимается попытка чтения или изменения сохранённого значения объекта типа DynamicType через glvalue типа AliasedType, поведение не определено, если не выполняется одно из следующих условий:
-
AliasedTypeиDynamicTypeявляются аналогичными. -
AliasedTypeявляется (возможно, cv-квалифицированным) знаковым или беззнаковым вариантомDynamicType. -
AliasedTypeэтоstd::byte,(с C++17) char или unsigned char: это позволяет исследовать представление объекта как массив байтов.
Это правило позволяет анализу алиасирования на основе типов, в котором компилятор предполагает, что значение, считанное через glvalue одного типа, не изменяется записью в glvalue другого типа (с учётом исключений, указанных выше).
Обратите внимание, что многие компиляторы C++ ослабляют это правило как нестандартное расширение языка, чтобы разрешить доступ к неправильному типу через неактивный член union (такой доступ не неопределён в C).
Примечания
Предполагая, что требования к выравниванию соблюдены, 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 reflector). Таким образом, любая техника, которая, по-видимому, способна создать такую ситуацию, неизбежно приводит к неопределённому поведению.
Когда нужно интерпретировать байты объекта как значение другого типа, можно использовать 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:
-
AliasedType— это агрегированный тип или тип объединения, который содержит один из упомянутых типов в качестве элемента или нестатического члена (включая, рекурсивно, элементы подмножеств и нестатические данные членов содержащихся объединений). -
AliasedType— это (возможно, квалифицированный cv)-базовый класс дляDynamicType.
Эти пункты описывают ситуации, которые не могут возникнуть в C++, и поэтому опущены из обсуждения выше. В C агрегированная копия и присваивание обращаются к агрегированному объекту целиком. Однако в C++ такие действия всегда выполняются через вызов члена функции, который обращается к отдельным подобъектам, а не к целому объекту (или, в случае объединений, копирует представление объекта, т.е. через unsigned char). Эти пункты были в конечном итоге удалены с помощью CWG issue 2051.
Пример
Демонстрирует некоторые способы использования 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 мог только преобразовывать l-значения в типы ссылок | Разрешены также x-значения |
См. также
Преобразование 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/w/cpp/language/reinterpret_cast