Kiedy stosować reinterpretację odlewu?

Jestem trochę mylony z możliwością zastosowania reinterpret_cast vs static_cast. Z tego co czytałem ogólne zasady to używanie statycznych odlewów, gdy typy mogą być interpretowane w czasie kompilacji stąd słowo static. Jest to cast, którego kompilator C++ używa wewnętrznie również do niejawnych odlewów.

reinterpret_casts mają zastosowanie w dwóch scenariuszach, konwersji typów całkowitych na typy wskaźników i odwrotnie lub konwersji jednego typu wskaźnika na inny. Ogólnie rzecz biorąc, rozumiem, że jest to nieopłacalne i powinno być / align = "left" /

Gdzie jestem trochę zdezorientowany, jest jedno użycie, które potrzebuję, wołam C++ z C i Kod C musi trzymać się obiektu C++, więc zasadniczo zawiera void*. Jaki odlew powinien być użyty do konwersji pomiędzy typem void * a typem klasy?

Widziałem użycie obu static_cast i reinterpret_cast? Chociaż z tego co czytam to wygląda na to, że static jest lepsze, ponieważ Obsada może się zdarzyć w czasie kompilacji? Chociaż mówi, aby użyć reinterpret_cast do konwersji z jednego typu wskaźnika na inny?

 369
Author: MD XF, 2009-02-21

10 answers

Standard C++ gwarantuje:

static_casting wskaźnik do i od void* zachowuje adres. Oznacza to, że w poniższych punktach a, b i c wszystkie wskazują na ten sam adres:

int* a = new int();
void* b = static_cast<void*>(a);
int* c = static_cast<int*>(b);

reinterpret_cast gwarantuje tylko, że jeśli rzucisz wskaźnik do innego typu, , a następnie reinterpret_cast z powrotem do oryginalnego typu , otrzymasz oryginalną wartość. Więc w następujący sposób:

int* a = new int();
void* b = reinterpret_cast<void*>(a);
int* c = reinterpret_cast<int*>(b);

A i c zawierają tę samą wartość, ale wartość b jest nieokreślona. (w praktyce będzie zazwyczaj zawierają ten sam adres co a i c, ale nie jest to określone w standardzie i może nie być prawdą na komputerach z bardziej złożonymi systemami pamięci.)

Do i z void*, static_cast powinny być preferowane.

 362
Author: jalf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-22 17:46:42

Jednym z przypadków, gdy reinterpret_cast jest konieczne, jest połączenie z nieprzezroczystymi typami danych. Często zdarza się to w interfejsach API dostawców, nad którymi programista nie ma kontroli. Oto wymyślony przykład, w którym Dostawca udostępnia API do przechowywania i pobierania dowolnych danych globalnych:

// vendor.hpp
typedef struct _Opaque * VendorGlobalUserData;
void VendorSetUserData(VendorGlobalUserData p);
VendorGlobalUserData VendorGetUserData();

Aby korzystać z tego API, programista musi przesłać swoje dane do VendorGlobalUserData i z powrotem. static_cast nie działa, trzeba użyć reinterpret_cast:

// main.cpp
#include "vendor.hpp"
#include <iostream>
using namespace std;

struct MyUserData {
    MyUserData() : m(42) {}
    int m;
};

int main() {
    MyUserData u;

        // store global data
    VendorGlobalUserData d1;
//  d1 = &u;                                          // compile error
//  d1 = static_cast<VendorGlobalUserData>(&u);       // compile error
    d1 = reinterpret_cast<VendorGlobalUserData>(&u);  // ok
    VendorSetUserData(d1);

        // do other stuff...

        // retrieve global data
    VendorGlobalUserData d2 = VendorGetUserData();
    MyUserData * p = 0;
//  p = d2;                                           // compile error
//  p = static_cast<MyUserData *>(d2);                // compile error
    p = reinterpret_cast<MyUserData *>(d2);           // ok

    if (p) { cout << p->m << endl; }
    return 0;
}

Poniżej znajduje się wymyślona implementacja przykładowego API:

// vendor.cpp
static VendorGlobalUserData g = 0;
void VendorSetUserData(VendorGlobalUserData p) { g = p; }
VendorGlobalUserData VendorGetUserData() { return g; }
 126
Author: jwfearn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-02 21:16:06

Krótka odpowiedź: Jeśli nie wiesz, co oznacza reinterpret_cast, nie używaj go. Jeśli będziesz tego potrzebował w przyszłości, będziesz wiedział.

Pełna odpowiedź:

Rozważmy podstawowe typy liczbowe.

Podczas konwersji na przykład int(12) na unsigned float (12.0f) twój procesor musi wywołać pewne obliczenia, ponieważ obie liczby mają inną reprezentację bitową. To jest to, co static_cast oznacza.

Z drugiej strony, kiedy dzwonisz reinterpret_cast na Procesor nie wywołuje żadnych obliczeń. Traktuje po prostu zestaw bitów w pamięci, jakby miał inny typ. Więc kiedy przekonwertujesz int* na float* za pomocą tego słowa kluczowego, nowa wartość (po dereferecingu wskaźnika) nie ma nic wspólnego ze starą wartością w znaczeniu matematycznym.

przykład: to prawda, że reinterpret_cast nie jest przenośny z jednego powodu-porządku bajtów (endianness). Ale jest to często zaskakująco najlepszy powód, aby go używać. Wyobraźmy sobie przykład: musisz odczytaj binarny numer 32bit z pliku i wiesz, że to big endian. Twój kod musi być ogólny i działa poprawnie na systemach big endian (np. ARM) i little endian (np. x86). Więc musisz sprawdzić kolejność bajtów. Jest dobrze znany w czasie kompilacji, więc można napisać constexpr function:

constexpr bool is_little_endian() {
  std::uint16_t x=0x0001;
  auto p = reinterpret_cast<std::uint8_t*>(&x);
  return *p != 0;
}

Wyjaśnienie: binarna reprezentacja x w pamięci może być 0000'0000'0000'0001 (big) lub 0000'0001'0000'0000 (little endian). Po reinterpretacji-rzuceniu bajtu pod p Wskaźnik może być odpowiednio 0000'0000 lub 0000'0001. Jeśli używasz odlewania statycznego, zawsze będzie 0000'0001, bez względu na endianness jest używany.

 50
Author: jaskmar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-28 09:28:57

Znaczenie reinterpret_cast nie jest zdefiniowane przez standard C++. Stąd teoretycznie reinterpret_cast może spowodować awarię Twojego programu. W praktyce Kompilatory starają się robić to, czego oczekujesz, czyli interpretować bity tego, co przekazujesz, tak jakby były typem, na który rzucasz. Jeśli wiesz, co Kompilatory, których używasz, robią z reinterpret_cast, możesz go użyć, ale mówienie, że jest Przenośny , byłoby kłamstwem.

Dla sprawy, którą opisujesz, i praktycznie każdego przypadku, w którym możesz rozważyć reinterpret_cast, możesz zamiast tego użyć static_cast lub innej alternatywy. Między innymi standard ma to do powiedzenia o tym, czego można oczekiwać od static_cast (§5.2.9):

Wartość R typu "wskaźnik do CV void" może być jawnie przekonwertowana na wskaźnik do typu obiektu. Wartość typu pointer to object przekonwertowana na "pointer to CV void" i z powrotem do oryginalnego typu pointer będzie miała swoją oryginalną wartość.

Więc dla Twojego przypadku użycia, wydaje się dość jasne, że standaryzacja Komitet przeznaczony dla Ciebie static_cast.

 16
Author: flodin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-21 16:52:25

Jednym z zastosowań reinterpret_cast jest zastosowanie operacji bitowych do pływaków (IEEE 754). Jednym z przykładów był szybki Odwrotny trik pierwiastka kwadratowego:

Https://en.wikipedia.org/wiki/Fast_inverse_square_root#Overview_of_the_code

Traktuje binarną reprezentację float jako liczbę całkowitą, przesuwa ją w prawo i odejmuje od stałej, zmniejszając tym samym o połowę i negując wykładnik. Po przekształceniu z powrotem w pływak, poddaje się go Newtonowi-Raphsonowi iteracja w celu dokładniejszego przybliżenia:

float Q_rsqrt( float number )
{
    long i;
    float x2, y;
    const float threehalfs = 1.5F;

    x2 = number * 0.5F;
    y  = number;
    i  = * ( long * ) &y;                       // evil floating point bit level hacking
    i  = 0x5f3759df - ( i >> 1 );               // what the deuce? 
    y  = * ( float * ) &i;
    y  = y * ( threehalfs - ( x2 * y * y ) );   // 1st iteration
//  y  = y * ( threehalfs - ( x2 * y * y ) );   // 2nd iteration, this can be removed

    return y;
}

To zostało pierwotnie napisane w C, więc używa C casts, ale analogicznym C++ cast jest reinterpret_cast.

 10
Author: Adam P. Goucher,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-16 12:32:13

Możesz użyć reinterprete_cast, aby sprawdzić dziedziczenie podczas kompilacji.
Zobacz też: Używanie reinterpret_cast do sprawdzania dziedziczenia w czasie kompilacji

 2
Author: Martin R.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:50
template <class outType, class inType>
outType safe_cast(inType pointer)
{
    void* temp = static_cast<void*>(pointer);
    return static_cast<outType>(temp);
}

Próbowałem zawrzeć i napisałem prosty bezpieczny cast za pomocą szablonów. Zauważ, że to rozwiązanie nie gwarantuje wyświetlania wskaźników na funkcji.

 0
Author: Sasha Zezulinsky,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-27 15:52:14

Najpierw masz jakieś dane w określonym typie jak int tutaj:

int x = 0x7fffffff://==nan in binary representation

Następnie chcesz uzyskać dostęp do tej samej zmiennej co inny typ, taki jak float: Możesz zdecydować pomiędzy

float y = reinterpret_cast<float&>(x);

//this could only be used in cpp, looks like a function with template-parameters

Lub

float y = *(float*)&(x);

//this could be used in c and cpp

BRIEF: oznacza to, że ta sama pamięć jest używana jako inny typ. Możesz więc przekonwertować binarne reprezentacje pływaków jako typu int jak powyżej na pływaki. 0x80000000 jest -0 na przykład (mantysa i wykładnik są null, ale znak, msb, jest jeden. Działa to również w przypadku sobowtórów i długie podwójne.

OPTIMIZE: myślę, że reinterpret_cast będzie zoptymalizowany w wielu kompilatorach, podczas gdy c-casting jest wykonywany przez pointeraithmetic (wartość musi być skopiowana do pamięci, ponieważ wskaźniki nie mogą wskazywać na rejestry cpu).

Uwaga: w obu przypadkach należy zapisać rzuconą wartość w zmiennej przed rzuceniem! To makro może pomóc:

#define asvar(x) ({decltype(x) __tmp__ = (x); __tmp__; })
 0
Author: cmdLP,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-23 19:51:47

Szybka odpowiedź: użyj static_cast jeśli kompiluje, w przeciwnym razie ucieknij się do reinterpret_cast.

 -3
Author: Marius K,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-13 01:20:52

Przeczytaj FAQ ! Przechowywanie danych w C++ może być ryzykowne.

W C++ wskaźnik do obiektu można przekonwertować na void * bez żadnych odlewów. Ale to nieprawda. Potrzebujesz static_cast, Aby odzyskać oryginalny wskaźnik.

 -14
Author: dirkgently,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-13 01:21:54