Spec-Zone.ru › C++

reinterpret_cast преобразование

Преобразует между типами, интерпретируя внутреннее битовое представление.

Синтаксис

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

Возвращает значение типа тип-цели.

Объяснение

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

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

1) Выражение целочисленного, перечислимого, указатель или указатель-на-член типа может быть преобразовано к своему собственному типу. Результирующее значение будет таким же, как значение выражение.
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" на объект, обозначаемый выражение. Временная переменная не создаётся, ни одна копия не делается, ни один конструктор или функция преобразования не вызываются. К результирующей ссылке можно обращаться безопасно только если это разрешено правилами алиасирования типов (см. ниже).
7) Любой указатель на функцию может быть преобразован в указатель на функцию другого типа. Вызов функции через указатель на функцию другого типа не определён, но преобразование такого указателя обратно в указатель на исходную функцию даёт указатель на исходную функцию.
8) На некоторых реализациях (в частности, на любой системе совместимой с POSIX, как требуется dlsym), указатель на функцию может быть преобразован в void* или любой другой указатель объекта, или наоборот. Если реализация поддерживает преобразование в обоих направлениях, преобразование к исходному типу даёт исходное значение, в противном случае результирующий указатель не может быть разыменован или вызван безопасно.
9) Значение нулевого указателя любого типа указателя может быть преобразовано в любой другой тип указателя, что приводит к значению нулевого указателя этого типа. Обратите внимание, что константа нулевого указателя nullptr или любое другое значение типа std::nullptr_t не может быть преобразовано в указатель с помощью reinterpret_cast: для этой цели следует использовать неявное преобразование или static_cast.
10) Указатель на функцию-член может быть преобразован в указатель на другую функцию-член другого типа. Преобразование обратно к исходному типу даёт исходное значение, в противном случае результирующий указатель не может быть использован безопасно.
11) Указатель на член-объект некоторого класса T1 может быть преобразован в указатель на другой член-объект другого класса T2. Если выравнивание T2 не строже, чем выравнивание T1, преобразование обратно к исходному типу T1 даёт исходное значение, в противном случае результирующий указатель не может быть использован безопасно.

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

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

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

reinterpret_cast

Алиясирование типов

Всякий раз, когда предпринимается попытка чтения или изменения сохранённого значения объекта типа 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

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

(с 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 Выполняет проверенные полиморфные преобразования
Явные преобразования Разрешенные преобразования между типами
Стандартные преобразования Неявные преобразования из одного типа в другой
bit_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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API