reinterpret_cast
vs 의 적용 가능성과 혼동되지 않습니다 static_cast
. 내가 읽은 일반적인 규칙에서 컴파일 타임에 유형을 해석 할 수있을 때 정적 캐스트를 사용하는 것이 단어 static
입니다. 이것은 C ++ 컴파일러가 암시 적 캐스트에도 내부적으로 사용하는 캐스트입니다.
reinterpret_cast
두 가지 시나리오에 적용 할 수 있습니다.
- 정수 유형을 포인터 유형으로 변환하거나 그 반대로 변환
- 한 포인터 유형을 다른 포인터 유형으로 변환하십시오. 내가 얻는 일반적인 아이디어는 이것이 이식 불가능하며 피해야한다는 것입니다.
내가 약간 혼란스러워하는 곳에서 필요한 사용법 중 하나는 C에서 C ++을 호출하고 C 코드는 C ++ 객체를 잡아야하므로 기본적으로을 보유하고 void*
있습니다. void *
클래스 유형과 클래스 유형 을 변환하려면 어떤 캐스트를 사용해야 합니까?
난 둘의 사용 본 static_cast
과를 reinterpret_cast
? 내가 읽은 것에서 static
컴파일 타임에 캐스트가 발생할 수 있기 때문에 더 나은 것처럼 보입니까? reinterpret_cast
한 포인터 유형에서 다른 포인터 유형으로 변환하는 데 사용한다고 말하고 있습니까?
답변
C ++ 표준은 다음을 보장합니다.
static_cast
void*
주소 를 유지하려면 주소 를 유지하십시오. 즉, 다음에,이다 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);
a
와 c
같은 값을 포함하지만,의 값은 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_cast
CPU를 호출하면 계산이 호출되지 않습니다. 메모리에있는 비트 세트를 다른 유형이있는 것처럼 처리합니다. 당신이 변환 할 때 그래서 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);
}
템플릿을 사용하여 간단한 안전한 캐스트를 작성하고 작성했습니다. 이 솔루션은 함수에 포인터를 캐스트한다고 보장하지 않습니다.