reinterpret_cast는 언제 사용합니까? 불가능하며 피해야한다는 것입니다. 내가 약간

reinterpret_castvs 의 적용 가능성과 혼동되지 않습니다 static_cast. 내가 읽은 일반적인 규칙에서 컴파일 타임에 유형을 해석 할 수있을 때 정적 캐스트를 사용하는 것이 단어 static입니다. 이것은 C ++ 컴파일러가 암시 적 캐스트에도 내부적으로 사용하는 캐스트입니다.

reinterpret_cast두 가지 시나리오에 적용 할 수 있습니다.

  • 정수 유형을 포인터 유형으로 변환하거나 그 반대로 변환
  • 한 포인터 유형을 다른 포인터 유형으로 변환하십시오. 내가 얻는 일반적인 아이디어는 이것이 이식 불가능하며 피해야한다는 것입니다.

내가 약간 혼란스러워하는 곳에서 필요한 사용법 중 하나는 C에서 C ++을 호출하고 C 코드는 C ++ 객체를 잡아야하므로 기본적으로을 보유하고 void*있습니다. void *클래스 유형과 클래스 유형 을 변환하려면 어떤 캐스트를 사용해야 합니까?

난 둘의 사용 본 static_cast과를 reinterpret_cast? 내가 읽은 것에서 static컴파일 타임에 캐스트가 발생할 수 있기 때문에 더 나은 것처럼 보입니까? reinterpret_cast한 포인터 유형에서 다른 포인터 유형으로 변환하는 데 사용한다고 말하고 있습니까?



답변

C ++ 표준은 다음을 보장합니다.

static_castvoid*주소 를 유지하려면 주소 를 유지하십시오. 즉, 다음에,이다 a, b그리고 c같은 주소에 대한 모든 점 :

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

reinterpret_cast다른 유형에 대한 포인터를 캐스트 한 다음 reinterpret_cast원래 유형으로 되 돌리면 원래 값을 얻습니다. 따라서 다음에서 :

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

ac같은 값을 포함하지만,의 값은 b지정되지 않습니다. (실제로는 일반적으로 같은 주소를 포함 a하고 c있지만, 표준에 명시되지 않은 것, 그리고 더 복잡한 메모리 시스템과 시스템에 진실하지 않을 수 있습니다.)

과에서 주조를 들어 void*, static_cast선호한다.


답변

reinterpret_cast불투명 한 데이터 유형과 인터페이스 할 때 필요한 경우 가 있습니다. 프로그래머가 제어 할 수없는 공급 업체 API에서 자주 발생합니다. 다음은 공급 업체가 임의의 전역 데이터를 저장하고 검색하기위한 API를 제공하는 고안된 예입니다.

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

이 API를 사용하려면 프로그래머가 데이터를 VendorGlobalUserData다시 캐스팅해야 합니다. static_cast작동하지 않으므로 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;
}

다음은 샘플 API를 구현 한 것입니다.

// vendor.cpp
static VendorGlobalUserData g = 0;
void VendorSetUserData(VendorGlobalUserData p) { g = p; }
VendorGlobalUserData VendorGetUserData() { return g; }

답변

짧은 대답 :
당신이 무엇을 reinterpret_cast의미 하는지 모른다면 그것을 사용하지 마십시오. 나중에 필요할 경우 알 것입니다.

전체 답변 :

기본 숫자 유형을 고려해 봅시다.

int(12)를 들어 unsigned float (12.0f)프로세서 로 변환 할 때 두 숫자의 비트 표현이 다르기 때문에 일부 계산을 호출해야합니다. 이것이 static_cast의미 하는 바입니다.

반면에 reinterpret_castCPU를 호출하면 계산이 호출되지 않습니다. 메모리에있는 비트 세트를 다른 유형이있는 것처럼 처리합니다. 당신이 변환 할 때 그래서 int*float*이 키워드와 함께, (포인터 dereferecing 후) 새 값은 수학적 의미에서 이전 값과는 아무 상관이 없습니다.

예 :reinterpret_cast 바이트 순서 (엔디안)라는 한 가지 이유로 인해 이식성이없는것은 사실입니다. 그러나 이것은 놀랍게도 그것을 사용하는 가장 좋은 이유입니다. 예를 상상해 봅시다 : 파일에서 이진 32 비트 숫자를 읽어야하며, 그것이 빅 엔디안이라는 것을 알고 있습니다. 코드는 일반적이어야하며 빅 엔디안 (예 : 일부 ARM) 및 리틀 엔디안 (예 : x86) 시스템에서 제대로 작동합니다. 따라서 바이트 순서를 확인해야합니다. 당신이 쓸 수 있도록 컴파일 시간에 잘 알려진 constexpr기능 : 당신은이를 달성하기 위해 함수를 작성할 수 있습니다 :

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

설명 :x 메모리에서이진 표현은0000'0000'0000'0001(큰) 또는0000'0001'0000'0000(little 엔디안) 일 수 있습니다. 재 해석 캐스팅 후p포인터아래의 바이트는각각0000'0000또는0000'0001입니다. 정적 캐스팅을 사용하는 경우0000'0001어떤 엔디안 (endianness)을 사용하든항상그렇습니다.

편집하다:

첫 번째 버전에서는 예제 함수 is_little_endian를로 만들었습니다 constexpr. 최신 gcc (8.3.0)에서 잘 컴파일되지만 표준에 따르면 불법입니다. clang 컴파일러는 컴파일을 거부합니다 (정확합니다).


답변

의 의미 reinterpret_cast는 C ++ 표준에 의해 정의되지 않습니다. 따라서 이론적으로 reinterpret_cast는 프로그램을 중단시킬 수 있습니다. 실제로 컴파일러는 예상 한 것을 수행하려고 시도합니다. 즉, 전달하는 비트를 마치 캐스팅하려는 유형 인 것처럼 해석합니다. 사용할 컴파일러가 무엇인지 알고 있다면 reinterpret_cast 그것을 사용할 수 있지만 이식성 이 있다고 말하는 것은 거짓말 일 것입니다.

당신이 묘사하는 경우와 고려할 수있는 거의 모든 경우 대신에 다른 대안을 reinterpret_cast사용할 수 있습니다 static_cast. 무엇보다도 표준은 기대할 수있는 것에 대해 말하고 있습니다 static_cast(§5.2.9).

“pointer to cv void”유형의 rvalue는 객체 유형에 대한 포인터로 명시 적으로 변환 될 수 있습니다. “포인터에서 cv void 로의 포인터”로 변환 된 객체에 대한 포인터 유형의 값은 원래 포인터 유형으로 돌아갑니다.

따라서 유스 케이스의 경우 표준화위원회가 귀하가 사용하도록 의도 한 것이 분명해 보입니다 static_cast.


답변

reinterpret_cast의 한 가지 사용법은 비트 연산을 (IEEE 754) 부동 소수점에 적용하려는 경우입니다. 이에 대한 한 가지 예는 Fast Inverse Square-Root 트릭입니다.

https://ko.wikipedia.org/wiki/Fast_inverse_square_root#Overview_of_the_code

float의 이진 표현을 정수로 취급하고 오른쪽으로 이동하여 상수에서 빼서 지수를 반으로 줄입니다. float로 다시 변환 한 후에는이 근사값을 더 정확하게하기 위해 Newton-Raphson 반복이 적용됩니다.

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;
}

원래 C로 작성되었으므로 C 캐스트를 사용하지만 유사한 C ++ 캐스트는 reinterpret_cast입니다.


답변

컴파일시 reinterprete_cast를 사용하여 상속을 확인할 수 있습니다.
여기를보십시오 :
컴파일 타임에 상속을 확인하기 위해 reinterpret_cast 사용


답변

template <class outType, class inType>
outType safe_cast(inType pointer)
{
    void* temp = static_cast<void*>(pointer);
    return static_cast<outType>(temp);
}

템플릿을 사용하여 간단한 안전한 캐스트를 작성하고 작성했습니다. 이 솔루션은 함수에 포인터를 캐스트한다고 보장하지 않습니다.