문제

나는 책을 읽고 있고 그것을 발견했다 reinterpret_cast 직접 사용해서는 안되고 오히려 void*에 캐스팅되어야합니다. static_cast:

T1 * p1=...
void *pv=p1;
T2 * p2= static_cast<T2*>(pv);

대신에:

T1 * p1=...
T2 * p2= reinterpret_cast<T2*>(p1);

그러나 왜 이것이 직접 캐스트보다 더 나은지 설명을 찾을 수 없습니다. 누군가 나에게 설명을 주거나 답을 가리킬 수 있다면 대단히 감사하겠습니다.

미리 감사드립니다

추신 나는 무엇인지 알고 있습니다 reinterpret_cast 사용되었지만 이런 식으로 사용되는 것을 본 적이 없습니다.

도움이 되었습니까?

해결책

그러한 캐스트가 허용되는 유형의 경우 (예 : T1 포드 유형입니다 T2 ~이다 unsigned char), 접근 static_cast 표준에 의해 잘 정의되어 있습니다.

반면에, reinterpret_cast 전적으로 구현 정의되어 있습니다 - 당신이 그것을 얻는 유일한 보장은 당신이 다른 포인터 유형에 포인터 유형을 캐스트 한 다음 다시 돌아올 수 있다는 것입니다. 또한 포인터 유형을 포인터 값 (구현에 따라 다르며 전혀 필요하지 않음)을 유지하기에 충분한 적분 유형에 포인터 유형을 캐스트 한 다음 다시 캐스트 할 수 있습니다.

보다 구체적으로, 나는 중요한 부분을 강조하고 표준의 관련 부분을 인용합니다.

5.2.10 [expr.reinterpret.cast] :

RenterPret_cast에 의해 수행되는 매핑은 다음과 같습니다 구현 정의. [참고 : 원래 값과 다른 표현을 생성 할 수도 있고 그렇지 않을 수도 있습니다.] ... 객체에 대한 포인터는 다른 유형의 객체로 포인터로 명시 적으로 변환 될 수 있습니다.) "T1에 대한 포인터"유형 "T2에 대한 포인터"(T1 및 T2는 객체 유형이고 T2의 정렬 요구 사항이 T1의 정렬 요구 사항보다 엄격하지 않은 경우 원래 유형으로 돌아가는 원래 포인터 값을 산출합니다. 이러한 포인터 변환의 결과는 지정되지 않습니다.

그래서 다음과 같은 것 :

struct pod_t { int x; };
pod_t pod;
char* p = reinterpret_cast<char*>(&pod);
memset(p, 0, sizeof pod);

효과적으로 지정되지 않습니다.

이유를 설명합니다 static_cast 작품은 조금 더 까다 롭습니다. 다음은 사용하도록 다시 작성한 위의 코드입니다 static_cast 나는 항상 표준에 따라 항상 작동한다고 생각합니다.

struct pod_t { int x; };
pod_t pod;
char* p = static_cast<char*>(static_cast<void*>(&pod));
memset(p, 0, sizeof pod);

다시 말하지만, 위의 내용이 휴대 가능하다고 결론을 내리는 표준의 섹션을 인용하겠습니다.

3.9 [BASIC.TYPES] :

POD Type T의 모든 객체 (기본 클래스 서브 버젝트 제외)의 경우 객체가 유형 T의 유효한 값을 보유하든 아니든, 객체를 구성하는 기본 바이트 (1.7)는 char 또는 부호없는 배열로 복사 할 수 있습니다. 숯. 숯 또는 부호없는 char 배열의 내용이 객체에 다시 복사되는 경우, 객체는 그 후 원래 값을 유지해야합니다.

T 형 객체의 객체 표현은 n 부호없는 char의 순서입니다. 사물 n은 t 형의 물체에 의해 촬영되며, 여기서 n은 (t) 크기와 같다.

3.9.2 [Basic.compound] :

CV 자격 (3.9.3) 또는 CV-Unqualified 유형의 물체 void* (void에 대한 포인터)는 알려지지 않은 유형의 물체를 가리키는 데 사용될 수 있습니다. ㅏ void* 객체 포인터를 잡을 수 있어야합니다. CV 자격 또는 CV- 자격 (3.9.3) void* CV 자격 또는 CV-Unqualified와 동일한 표현 및 정렬 요구 사항이 있어야합니다. char*.

3.10 [Basic.lval] :

프로그램이 다음 유형 중 하나 이외의 LValue를 통해 객체의 저장된 값에 액세스하려고 시도하면 동작이 정의되지 않습니다) :

  • ...
  • 문자 또는 서명되지 않은 숯 유형.

4.10 [conv.ptr] :

T는 "CV T에 대한 포인터"유형의 RValue를 객체 유형 인 "CV void에 대한 포인터"유형의 RValue로 변환 할 수 있습니다. "포인터"를 CV T로 변환 한 결과 객체가 타입 T의 가장 파생 된 객체 (1.8) 인 것처럼 T 형의 객체가 존재하는 스토리지 위치의 시작을 가리 킵니다. (즉, 기본 클래스 하위 목체가 아닙니다).

5.2.9 [expr.static.cast] :

LValue-to-RValue (4.1), Array-Topointer (4.2), 함수-포인터 (4.3) 및 부울 (4.12) 전환 이외의 표준 변환 시퀀스 (4 항)의 역수를 수행 할 수 있습니다. static_cast를 명시 적으로 사용합니다.

편집하다 반면에, 우리는이 보석을 가지고 있습니다.

9.2 [class.mem]/17 :

reinterpret_cast를 사용하여 적절하게 변환 된 포드 스트럭 객체에 대한 포인터는 초기 멤버를 가리 키십시오 (또는 해당 멤버가 비트 필드 인 경우, 그것이 상주하는 단위로) 그 반대도 마찬가지입니다. [참고 : 아마도있을 수 있습니다 그러므로 적절한 정렬을 달성하기 위해 필요에 따라 포드 스트럭 객체 내에서 이름이없는 패딩이지만 처음에는 그렇지 않습니다. ]

그것은 그것을 암시하는 것 같습니다 reinterpret_cast 포인터 사이에서 어떻게 든 "동일한 주소"를 의미합니다. 그림을 이동.

다른 팁

의도는 두 형태가 모두 잘 정의되어 있다는 것이 의심의 여지가 없지만 문구는 그것을 포착하지 못한다는 것입니다.

두 형태 모두 실제로 작동합니다.

reinterpret_cast 의도에 대해 더 명확하고 선호해야합니다.

이것이 진짜 이유는 C ++가 상속을 정의하는 방법과 멤버 포인터 때문입니다.

C를 사용하면 포인터는 주소 일뿐입니다. C ++에서는 일부 기능으로 인해 더 복잡해야합니다.

멤버 포인터는 실제로 클래스에 오프셋이므로 캐스트하는 것은 항상 C 스타일을 사용하여 재앙입니다.

콘크리트 부품이있는 두 가지 가상 객체에 상속 된 경우 C 스타일의 재난이기도합니다. 그러나 여러 상속의 경우 모든 문제를 일으키는 경우이므로 어쨌든 이것을 사용하고 싶지 않아야합니다.

정말로, 당신은 처음에 이러한 사례를 사용하지 않기를 바랍니다. 또한, 당신이 많은 것을 캐스팅하고 있다면, 당신은 당신의 디자인에서 엉망이되는 또 다른 표시입니다.

내가 캐스팅을 끝내는 유일한 시간은 C ++가 동일하지는 않지만 분명히 필요한 곳의 프리미티브와 함께하는 것입니다. 실제 객체의 경우, 무언가를 캐스팅하고 싶을 때마다 대부분의 시간 동안 '인터페이스에 프로그래밍해야하기 때문에 디자인에 의문을 제기하십시오. 물론, 타사 API가 어떻게 작동하는지 변경할 수 없으므로 항상 많은 선택이 없습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top