2010-11-28 4 views
13

회원 전환에 대한 포인터와 관련된 C++ 03 표준 초안에서 다음 단락을 발견했습니다. 부재 전환에회원 전환에 대한 포인터

4.11/2 포인터

B 클래스 타입 인 "타입 CV T의 B의 부재 포인터 '유형의 r- 수치는 형태의 r- 수치로 변환 할 수

"Cv T 유형의 D 멤버에 대한 포인터"는 D가 B의 파생 클래스 (10 절) 인 경우 B가 액세스 할 수 없거나 (11 절) 모호한 (10.2) 또는 D의 가상 (10.1) 기본 클래스 인 경우 이 변환을 필요로하는 프로그램은 부적절합니다. 변환 결과는 변환되기 전의 멤버에 대한 포인터와 동일한 멤버를 참조하지만 기본 클래스 멤버를 파생 클래스의 멤버 인 것처럼 참조합니다. 결과는 D의 B 인스턴스에있는 멤버를 참조합니다. 결과에 "cv T 유형의 D 멤버에 대한 포인터"유형이 있으므로 D 개체로 역 참조 할 수 있습니다. 결과는 B 구성원에 대한 포인터가 D의 B 하위 오브젝트로 역 참조 된 것과 같습니다. 널 (null) 구성원 포인터 값은 대상 유형의 널 (null) 구성원 포인터 값으로 변환됩니다 .52)

5.2.9/9 static_cast

"타입 CV1 (T)의 D의 부재 포인터 '유형의 r- 수치는"타입 CV2의 T의 B의 멤버에 대한 포인터 "형태의 r- 수치로 전환시킬 수있다 B는 "T 타입의 B의 멤버에 대한 포인터"에서 "T 타입의 D의 멤버에 대한 포인터"로의 유효한 표준 변환이 존재하고 (4.11), cv2가 동일한 cv 인 경우, D의 기본 클래스 (10 절) -자격 as 또는 cv-qualification보다 큰 cv-qualification) null 멤버 포인터 값 (4.11)은 대상 유형의 null 멤버 포인터 값으로 변환됩니다. 클래스 B가 원래 멤버를 포함하거나 원래 멤버를 포함하는 클래스의 기본 클래스 또는 파생 클래스 인 경우 멤버에 대한 결과 포인터는 원래 멤버를 가리 킵니다. 그렇지 않으면 형 변환의 결과가 정의되지 않습니다. [참고 : 클래스 B는 에 원래 멤버가 포함되어 있지 않아도 멤버에 대한 포인터가 역 참조되는 동적 유형에는 원래 멤버가 있어야합니다. 5.5 참조. ]

여기 내 질문이 있습니다. 5.2.9/9에서 말한 것처럼 4.11/2에 기술 된 유효한 변환이 존재하면 D의 멤버에 대한 포인터를 B의 멤버에 대한 포인터로 변환 할 수 있습니다. 이것은 B에서 상속받지 않은 D의 멤버 'm'이있는 경우 멤버 'm'에 대한 포인터를 B 멤버에 대한 포인터 유형으로 형변환 할 수 없다는 것을 의미합니까?

5.2.9/9의 노트에서
class Base { }; 
class Derived : public Base 
{ 
    int a; 
}; 
typedef int Base::* BaseMemPtr; 
BaseMemPtr pa = static_cast<BaseMemPtr>(&Derived::a); // invalid, as per 5.2.9/9 ? 

, 그것은 또한 말한다 클래스 B는 원래 회원, 회원에 대한 포인터가 원래 멤버를 포함해야합니다 역 참조되는 객체의 동적 유형을 포함 할 필요가 있지만, .

단락의 문구와 혼동합니다. 위의 코드가 유효합니까?

사이트를 검색 한 결과 비슷한 질문 인 c++ inheritance and member function pointers가 있는데, 포인터에서 기본 클래스 멤버로의 변환에서 파생 클래스 멤버에 대한 포인터로 변환하는 경우에만 해당됩니다.

+0

데이터 멤버 값에 대한 포인터를 멤버 함수 변수에 대한 포인터에 할당합니다. 그렇지 않으면, 옙, "캐스트 결과가 정의되지 않았습니다." – Potatoswatter

+0

고마워, 고쳤어. – ashen

답변

10

작성한 코드는 완벽하게 유효합니다. 그것에는 아무런 문제가 없습니다 (Derived::a은 사적인 것 외에도). 그것은 형식이 좋고 행동이 정의되어 있습니다 (지금까지). 표준의 인용 부분에서 밝히고 있듯이 정확히 명시한 static_cast을 사용하여 회원 포인터를 업 케하는 것은 완전히 합법적입니다. 5.2.9/9는 뾰족한 멤버가 기본 클래스에 있어야한다는 것을 결코 말하지 않습니다.올바르게 표준에서 인용

은 또한, 물체의 실제 멤버의 존재는없는 초기의 순간에, 포인터의 역 참조의 순간에 나중에 필요합니다. 이것은 물론 구성원 액세스 연산자 (->* 또는 .*)의 왼쪽에 사용되는 개체의 동적 유형에 따라 다릅니다. 형식은 런타임에만 알려 지므로 컴파일러에서 확인할 수 없습니다.

이 요구 사항은 5.2.9/9로 단순한 참고로 포함되어 있지만 개체 의 동적 유형이 포함되지 않은 경우는 5.5/4

4 더 공식적인 형태로 반복된다 포인터가 참조하는 멤버 인 경우 동작은 입니다.

그래서, 그러나

Base b; 
b.*pa; // 1 

Derived d; 
d.*pa; // 2 

Base *pb = &d; 
pb->*pa; // 3 

을 잘 형성 예를 들어, 예제의 컨텍스트에서 코드의 다음 줄이되어, 첫 번째 역 참조는 객체 b는 멤버를 포함하지 않기 때문에 정의되지 않은 동작을 (생산), 둘째와 셋째는 모두 합법적입니다.

+0

우수 답변. 그러나 sizeof b. * pa는 존재하지 않는 멤버의 "참조 해제"에도 불구하고 정의 된 동작을 제공함을 언급하는 것에 저항 할 수 없습니다. (왜냐하면 C++의 어떤 것도 간단하지 않기 때문에 ... :-P) –

+1

@j_random_hacker : 그렇습니다. 제 경우에는 정의되지 않은 행동의 발생은 표현식의 * 평가 * 동안 포인터 역 참조 과정에 묶여 있습니다. 반면에'sizeof'의 경우에는 인자가 평가되지 않습니다. – AnT

+0

VC는 C4407 경고를 내 보낸다. 파생 클래스의 함수 멤버에 대한 포인터를 두 번째 기본 유형에 정적으로 캐스트하면 표준 및 응답에 따라 표준을 준수하지 않는 것인가? – ashen

관련 문제