2014-07-23 4 views
5

가상 함수가 관련되어 있지 않을 때 가상 기본 클래스에서 파생 클래스로 다운 캐스트 할 수 있습니까? 제가 이야기하는 것을 보여줄 수있는 몇 가지 코드가 있습니다 :비 다형성 가상 기본 클래스에서 다운 캐스트하는 방법은 무엇입니까?

struct Base1 
{ 
    int data; 
}; 

struct Base2 
{ 
    char odd_size[9]; 
}; 

struct ViBase 
{ 
    double value; 
}; 


struct MostDerived : Base1, Base2, virtual ViBase 
{ 
    bool ok; 
}; 


void foo(ViBase &v) 
{ 
    MostDerived &md = somehow_cast<MostDerived&>(v); //but HOW? 
    md.ok = true; 
} 


int main() 
{ 
    MostDerived md; 
    foo(md); 
} 

코드는 데모 용입니다. 제 실제 시나리오는 상당히 복잡하며 템플릿 매개 변수와 캐스팅을 포함하고 있습니다. 첫 번째 매개 변수는 두 번째 매개 변수의 기본 매개 변수입니다. 그것은 정상 또는 가상베이스가 될 수 있으며 가상 기능이 있거나 없을 수 있습니다. (하단의 단순화 된 예 참조). 유형 특성을 사용하여 다형성 케이스와 가상/비 가상 기본 케이스를 감지하고 비 다형성 가상베이스를 제외한 모든 것을 해결할 수 있습니다. 그래서 그것이 제가 요구하는 것입니다.

정말 캐스트 할 수있는 방법을 생각할 수 없다

:

  • 암시 적 변환이 바로 밖으로을; 이들은 단지 업 캐스트를합니다.

  • static_cast 명시 적으로 가상 기본 클래스에서 주조 금지 :

    5.2.9/2 ... 그리고 BD의 가상 기본 클래스와 가상의 기본 클래스도 아니다 기본 클래스는 D입니다. ...

  • dynamic_cast는 다운 캐스트가

    5.2.7/6 그렇지 않으면 다형성 클래스를 필요로 v는 포인터 또는의 glvalue한다, 하나 그것을 할 수 없습니다 다형성 유형 (10.3).

    10.3/1 ... 가상 함수를 선언하거나 상속하는 클래스를 다형 클래스라고합니다.

  • reinterpret_cast이 전혀 적용되지 않습니다.

MostDerived 경우 이것은 물론 dynamic_cast 해결 될 수있는, 적어도 하나 개의 가상 함수를했다. 그러나 그렇지 않다면 캐스트를 할 수있는 방법이 있습니까?

너무 많은 위의 예제 코드에 초점 의견에 비추어


, 여기 내 실제 상황이 무엇인지의 스케치입니다 (모든 인용 부호는 C++ 11 초안 N3485에서 발췌 한 참고) 이다

template <class T_MostDerived> 
struct Bar 
{ 
    template <class T_Base> 
    void foo(T_Base &b, typename std::enable_if<std::is_base_of<T_Base, T_MostDerived>::value>::type * = nullptr) 
    { 
    T_MostDerived &md = somehow_cast<T_MostDerived>(b); 
    do_stuff_with(md); 
    } 
}; 

, 나는 T_BaseT_MostDerived의 기본 클래스는 것을 알고 (내가 T_MostDerived 정말 가장 파생 형 것을 알고),하지만 난 그들에 대해 아무 것도 모른다; Bar은 알 수없는 클라이언트가 사용할 수있는 라이브러리의 일부인 내 코드입니다.나는 이 아닌 다형성 가상베이스라고 탐지 할 수는 있지만, 을 그런 경우에 캐스트 할 수는 없습니다.

+0

정확히'ViBase' 다형성을 만들 수없는 이유는 무엇입니까? (예 : 소멸자를 가상으로 만들 수 있음) – pyon

+3

ViBase가 다형성이 아닌 경우 가상 기본 클래스로 사용하는 이유는 무엇입니까? – Niall

+0

'virtual' 키워드를 제거하십시오. 그럼 당신은 지금 일반 structs 다루고있는 참조/포인터를 행복하게 reinterpret ... – Anonymous

답변

4

MostDerived&에서 ViBase&으로 암시 적으로 명백한 변환이 있습니다. static_cast은 이러한 변환을 명시 적으로 표현할 수 있으며 반대의 변환도 수행 할 수 있습니다. 이 ’은 static_cast의 전환 유형입니다.

OP가 가상베이스에서 static_cast으로 떨어진 점은 잘못되었습니다.

#include <iostream> 
using namespace std; 

struct B { virtual ~B(){} }; 
struct D: virtual B {}; 
struct E: virtual B {}; 
struct X: D, E {}; 

auto main() -> int 
{ 
    X x; 
    B& b = static_cast<E&>(x); 

    // Can't do the following for the address adjustment that would work for 
    // D sub-object won't work for E sub-object, yet declarations of D and E 
    // are identical -- so the address adjustment can't be inferred from that. 
    // 
    //static_cast<D&>(b); 

    // This is OK: 
    dynamic_cast<D&>(b); 
} 

는 본질적으로,이 볼 수 있듯이, 당신은 혼자 D (또는 E)의 선언에서 주소 조정을 추론 할 수 없습니다 아래

소스 코드는 이유를 설명한다. 컴파일러도 마찬가지입니다. 이것은 또한 reinterpret_cast을 배제합니다.

+0

참 (그리고 +1)이지만 그것은 더 많은 관찰입니다. 나는 그 대답을 "방법이 없다"고 읽을 것인가? 아마 당신은 그것을 더 명백하게 진술 할 수 있습니까? – Angew

+1

@Angew : 비 다형성 가상 기본 클래스에서 잘 정의 된 효과를 사용하여 모든 캐스팅 방법을 다 써버 렸기 때문에 아무 것도 없습니다. 그렇다고해서 "아무 의미가 없다"는 의미는 아니지만, 무엇을 하든지간에 비용이 들게된다는 것을 의미합니다. (공식적으로 UB 코드 용) 또는 복잡성 및 메모리 풋 프린트 (모든 'Base'객체를 동적으로 등록), 또는 효율성 (알려진 패턴 스캔 용) 등이 포함됩니다. 초점을 맞추어야 할 주된 문제는 정적으로 알려진 유형의 비 다형성 가상 기본 클래스가 적절한 유형을 유지하는 디자인 수준의 실패를 나타냅니다. 나는 그것을 본다. –

2

해킹이 필요합니다. 다운 캐스트는 다중 상속이 기본 클래스를 파생 클래스 내의 임의의 위치에 배치 할 수 있기 때문에 수학이 필요합니다. 그러나 기본 클래스가 가상 상속 받았다는 것을 알고 있으면 파생 클래스에 인스턴스가 하나만 있어야합니다. 이렇게하면 변환 기능을 만들 수 있다는 것을 의미합니다 :

struct MostDerived : Base1, Base2, virtual ViBase 
{ 
    bool ok; 
    template <typename T> static MostDerived * somehow_cast (T *v) { 
    static MostDerived derived; 
    static T &from = derived; 
    static size_t delta 
     = reinterpret_cast<char *>(&from) - reinterpret_cast<char *>(&derived); 
    char *to = reinterpret_cast<char *>(v); 
    return reinterpret_cast<MostDerived *>(to - delta); 
    } 
}; 

특별 C++의 캐스트가이 기능은 안전을 입력하지 않는 당신을 줄 무엇인가. 이 함수는 ViBase에 전달 된 적절한 파생 된 자식이 캐스팅 될 것으로 맹목적으로 가정합니다. 이는 일반적으로 적용되지 않습니다.

+1

여기서 제약 조건은 '* v' 객체의 가장 많이 파생 된 객체가 실제로는 MostDerived이고 MostDerived에서 파생 된 클래스가 아니라는 것을 알아야한다는 것입니다. –

+0

@ Cheersandhth.-Alf +1, 일반적으로 사실입니다. 다행히도, 나의 실제 경우에, 나는 그것을 확실히 알 것이다. – Angew

+0

+1; 그게 내가하려는 계획이야. 불행히도 기술적으로 정의되지 않은 동작입니다. – Angew