reinterpret_cast
conversion
기본 비트 패턴을 재해석하여 타입 간 변환을 수행합니다.
목차 |
구문
reinterpret_cast<
대상-유형
>(
표현식
)
|
|||||||||
target-type 타입의 값을 반환합니다.
설명
static_cast 와는 달리, 그러나 const_cast 와 마찬가지로, reinterpret_cast 표현식은 어떤 CPU 명령어로도 컴파일되지 않습니다 (정수와 포인터 간 변환, 또는 포인터 표현이 타입에 따라 달라지는 특수 아키텍처에서 포인터 간 변환인 경우 제외). 이는 주로 컴파일 타임 지시자로서, 컴파일러가 expression 을 마치 target-type 타입을 가진 것처럼 처리하도록 지시합니다.
다음 변환만이 reinterpret_cast 로 수행될 수 있으며, 이러한 변환이 constness 제거 (또는 volatility 제거)를 유발하는 경우는 예외입니다.
static_cast
또는
implicit conversion
을 사용해야 합니다.
|
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
타입의
lvalue
(C++11 이전)
glvalue
(C++11 이후)
표현식은 다른 타입
T2
에 대한 참조로 변환될 수 있습니다. 결과는
*
reinterpret_cast
<
T2
*
>
(
p
)
의 결과와 동일하며, 여기서
p
는
표현식
이 지정하는 객체나 함수를 가리키는 "pointer to
T1
" 타입의 포인터입니다. 임시 객체가
구체화되거나 생성되지 않으며
(C++17 이후)
, 복사본이 생성되지 않고, 생성자나 변환 함수가 호출되지 않습니다. 결과 참조는
타입 접근 가능
한 경우에만 안전하게 접근할 수 있습니다.
dlsym
에 의해 요구되는 대로)에서는 함수 포인터를
void
*
또는 다른 객체 포인터로 변환하거나 그 반대로 변환할 수 있습니다. 구현체가 양방향 변환을 지원하는 경우, 원본 타입으로 변환하면 원본 값을 얻지만, 그렇지 않으면 결과 포인터를 역참조하거나 안전하게 호출할 수 없습니다.
T1
의 멤버 객체에 대한 포인터는 다른 클래스
T2
의 다른 멤버 객체에 대한 포인터로 변환될 수 있습니다.
T2
의 정렬 요구사항이
T1
의 정렬 요구사항보다 엄격하지 않은 경우, 원래 타입
T1
으로 다시 변환하면 원래 값을 얻을 수 있습니다. 그렇지 않으면 결과 포인터를 안전하게 사용할 수 없습니다.
모든 형변환 표현식과 마찬가지로, 결과는 다음과 같습니다:
- target-type 이 lvalue 참조 타입인 경우 lvalue 또는 함수 타입에 대한 rvalue 참조인 경우 (C++11부터) ;
|
(since C++11) |
- 그렇지 않으면 prvalue입니다.
타입 별칭
타입 접근성
만약 타입
T_ref
가 다음 타입들 중 하나와
유사(similar)
하다면,
동적 타입(dynamic type)
T_obj
인 객체는
T_ref
타입의
lvalue
(C++11까지)
glvalue
(C++11부터)
를 통해
타입 접근 가능(type-accessible)
합니다:
- char , unsigned char 또는 std::byte (C++17 이후) : 이를 통해 모든 객체의 객체 표현 을 바이트 배열로 검사할 수 있습니다.
-
T_obj -
T_obj에 해당하는 부호 있는 또는 부호 없는 타입
프로그램이 형식 접근이 불가능한 lvalue (until C++11) glvalue (since C++11) 를 통해 객체의 저장된 값을 읽거나 수정하려고 시도하는 경우, 그 동작은 정의되지 않습니다.
이 규칙은 타입 기반 별칭 분석을 활성화합니다. 여기서 컴파일러는 한 타입의 glvalue를 통해 읽은 값이 다른 타입의 glvalue에 대한 쓰기로 수정되지 않는다고 가정합니다(위에서 언급한 예외 사항 적용).
많은 C++ 컴파일러가 비표준 언어 확장으로 이 규칙을 완화하여, union 의 비활성 멤버를 통해 잘못된 타입으로의 접근을 허용한다는 점에 유의하십시오 (이러한 접근은 C에서는 undefined behavior가 아닙니다).
호환성 호출
다음 조건 중 하나라도 충족되면, 타입
T_call
는 함수 타입
T_func
와
호환 가능(call-compatible)
합니다:
-
T_call는T_func와 동일한 타입입니다.
|
(C++17부터) |
함수가 호출될 때 표현식의 function type 이 호출된 함수 정의의 타입과 호환되지 않는 경우, 그 동작은 정의되지 않습니다.
참고 사항
정렬 요구사항이 충족된다고 가정할 때, reinterpret_cast 는 포인터의 값 을 변경하지 않습니다. 단, pointer-interconvertible 객체를 다루는 몇 가지 제한된 경우는 예외입니다:
struct S1 { int a; } s1; struct S2 { int a; private: int b; } s2; // 표준 레이아웃이 아님 union U { int a; double b; } u = {0}; int arr[2]; int* p1 = reinterpret_cast<int*>(&s1); // p1의 값은 "s1.a를 가리키는 포인터"입니다. // s1.a와 s1은 포인터 상호 변환 가능하기 때문입니다 int* p2 = reinterpret_cast<int*>(&s2); // p2의 값은 reinterpret_cast에 의해 변경되지 않으며 // "s2를 가리키는 포인터"입니다 int* p3 = reinterpret_cast<int*>(&u); // p3의 값은 "u.a를 가리키는 포인터"입니다: // u.a와 u는 포인터 상호 변환 가능합니다 double* p4 = reinterpret_cast<double*>(p3); // p4의 값은 "u.b를 가리키는 포인터"입니다: u.a와 // u.b는 포인터 상호 변환 가능합니다. 왜냐하면 // 둘 다 u와 포인터 상호 변환 가능하기 때문입니다 int* p5 = reinterpret_cast<int*>(&arr); // p5의 값은 reinterpret_cast에 의해 변경되지 않으며 // "arr을 가리키는 포인터"입니다
적절한 타입의 객체를 실제로 지정하지 않는 glvalue(예: reinterpret_cast 를 통해 얻은 것)에 대해 비정적 데이터 멤버 또는 비정적 멤버 함수를 지정하는 클래스 멤버 접근을 수행하는 것은 정의되지 않은 행동을 초래합니다:
struct S { int x; }; struct T { int x; int f(); }; struct S1 : S {}; // 표준 레이아웃 struct ST : S, T {}; // 표준 레이아웃 아님 S s = {}; auto p = reinterpret_cast<T*>(&s); // p의 값은 "s를 가리키는 포인터" auto i = p->x; // 클래스 멤버 접근 표현식은 정의되지 않은 동작; // s는 T 객체가 아님 p->x = 1; // 정의되지 않은 동작 p->f(); // 정의되지 않은 동작 S1 s1 = {}; auto p1 = reinterpret_cast<S*>(&s1); // p1의 값은 "s1의 S 하위 객체를 가리키는 포인터" auto i = p1->x; // 정상 p1->x = 1; // 정상 ST st = {}; auto p2 = reinterpret_cast<S*>(&st); // p2의 값은 "st를 가리키는 포인터" auto i = p2->x; // 정의되지 않은 동작 p2->x = 1; // 정의되지 않은 동작
많은 컴파일러가 이러한 경우 "strict aliasing" 경고를 발생시키는데, 비록 기술적으로 그러한 구조가 일반적으로 "strict aliasing rule"로 알려진 문단보다 다른 것에 위배되더라도 그렇습니다.
strict aliasing 및 관련 규칙의 목적은 타입 기반 별칭 분석을 가능하게 하는 것입니다. 프로그램이 서로 관련 없는 타입에 대한 두 개의 포인터(예: int * 와 float * )가 동시에 존재하고 둘 다 동일한 메모리를 로드하거나 저장하는 데 사용될 수 있는 상황을 유효하게 생성할 수 있다면 이 분석이 크게 약화될 것입니다( SG12 리플렉터의 이 이메일 참조). 따라서 그러한 상황을 생성할 수 있는 것으로 보이는 어떤 기법도 필연적으로 미정의 동작을 유발합니다.
객체의 바이트를 다른 타입의 값으로 해석해야 할 때, 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); // 정의되지 않은 동작 std::memcpy(&n, &d, sizeof d); // 정상 n = std::bit_cast<std::int64_t>(d); // 또한 정상
|
구현체가 std::intptr_t 및/또는 std::uintptr_t 를 제공하는 경우, 객체 타입이나 cv void 에 대한 포인터를 이러한 타입으로의 캐스트는 항상 잘 정의됩니다. 그러나 함수 포인터에 대해서는 이것이 보장되지 않습니다. |
(C++11부터) |
C에서는 집합체 복사와 할당이 집합체 객체 전체에 접근합니다. 그러나 C++에서는 이러한 동작이 항상 멤버 함수 호출을 통해 수행되며, 이는 전체 객체가 아닌 개별 하위 객체에 접근합니다(또는 공용체의 경우 객체 표현을 복사합니다, 즉 unsigned char 를 통해).
키워드
예제
reinterpret_cast 의 몇 가지 사용 예시를 보여줍니다:
#include <cassert> #include <cstdint> #include <iostream> int f() { return 42; } int main() { int i = 7; // 포인터를 정수로 변환하고 다시 되돌리기 std::uintptr_t v1 = reinterpret_cast<std::uintptr_t>(&i); // static_cast는 오류 발생 std::cout << "The value of &i is " << std::showbase << std::hex << v1 << '\n'; int* p1 = reinterpret_cast<int*>(v1); assert(p1 == &i); // 함수 포인터를 다른 함수 포인터로 변환하고 다시 되돌리기 void(*fp1)() = reinterpret_cast<void(*)()>(f); // fp1(); 정의되지 않은 동작 int(*fp2)() = reinterpret_cast<int(*)()>(fp1); std::cout << std::dec << fp2() << '\n'; // 안전함 // 포인터를 통한 타입 앨리어싱 char* p2 = reinterpret_cast<char*>(&i); std::cout << (p2[0] == '\x7' ? "This system is little-endian\n" : "This system is big-endian\n"); // 참조를 통한 타입 앨리어싱 reinterpret_cast<unsigned int&>(i) = 42; std::cout << i << '\n'; [[maybe_unused]] const int &const_iref = i; // int &iref = reinterpret_cast<int&>( // const_iref); // 컴파일러 오류 - const 제거 불가 // const_cast를 사용해야 함: 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
가 참조 타입으로
lvalue만 캐스트 가능 |
xvalue도 허용 |
| CWG 2780 | C++98 |
reinterpret_cast
가 함수 lvalue를
다른 참조 타입으로 캐스트 불가 |
허용 |
| CWG 2939 | C++17 |
reinterpret_cast
가 prvalue를
rvalue 참조 타입으로 캐스트 가능 |
허용되지 않음 |
참고문헌
- C++23 표준 (ISO/IEC 14882:2024):
-
- 7.6.1.10 Reinterpret cast [expr.reinterpret.cast]
- C++20 표준(ISO/IEC 14882:2020):
-
- 7.6.1.9 재해석 변환 [expr.reinterpret.cast]
- C++17 표준 (ISO/IEC 14882:2017):
-
- 8.2.10 Reinterpret cast [expr.reinterpret.cast]
- C++14 표준(ISO/IEC 14882:2014):
-
- 5.2.10 Reinterpret cast [expr.reinterpret.cast]
- C++11 표준 (ISO/IEC 14882:2011):
-
- 5.2.10 Reinterpret cast [expr.reinterpret.cast]
- C++98 표준(ISO/IEC 14882:1998):
-
- 5.2.10 Reinterpret cast [expr.reinterpret.cast]
- C++03 표준(ISO/IEC 14882:2003):
-
- 5.2.10 Reinterpret cast [expr.reinterpret.cast]
참고 항목
const_cast
conversion
|
const 추가 또는 제거 |
static_cast
conversion
|
기본 변환 수행 |
dynamic_cast
conversion
|
검증된 다형성 변환 수행 |
| 명시적 캐스트 | 타입 간 허용적 변환 |
| 표준 변환 | 한 타입에서 다른 타입으로의 암시적 변환 |
|
(C++20)
|
한 타입의 객체 표현을 다른 타입의 표현으로 재해석
(함수 템플릿) |