2012-12-24 4 views
4

는 다음 사항을 고려하십시오는 C++ 11 동적 캐스트 -> 스위치

struct B { }; 

template<typename T> 
struct D : B 
{ 
    T t; 
} 

void g(int i) { ... } 
void g(string s) { ... } 
void g(char c) { ... } 

void f(B* b) 
{ 
    if (dynamic_cast<D<int>*>(b)) 
    { 
     g(dynamic_cast<D<int>*>(b)->t); 
    } 
    else if (dynamic_cast<D<string>*>(b)) 
    { 
     g(dynamic_cast<D<string>*>(b)->t); 
    } 
    else if (dynamic_cast<D<char>*>(b)) 
    { 
     g(dynamic_cast<D<char>*>(c)->t) 
    } 
    else 
     throw error; 
}; 
여기

이 T의 세 가지 유형이 있습니다 - INT, 문자열, 문자는 -하지만 가능한 유형의 목록이 있다면 더 길고, n을 말하면, if else chain은 O (n)을 수행 할 것입니다.

이 문제를 해결하는 방법 중 하나는 D에 여분의 형식 코드를 저장하고 형식 코드에 switch을 저장하는 것입니다.

RTTI 시스템에는 이미 이러한 코드가 있어야합니다. 거기에 접근 할 수있는 곳이 있나요?

아니면 내가하고 싶은 일을하는 더 좋은 방법이 있습니까?

+0

@ JoachimPileborg :이 장난감의 예에서 나는'f'를'struct D {virtual void f() {g (t)}}'로 대체 할 수 있지만 이것은 더 큰 문제를 놓치고 있습니다. –

답변

3

C++ 11은 거의입니다.

C++ 03에서는 컴파일 타임 상수 (case 필요)를 얻는 유일한 방법은 형식 시스템을 사용하기 때문에 불가능했습니다. typeid은 항상 동일한 유형을 반환하므로 switch에 대해 다른 대안을 생성 할 수 없습니다.

C++ 11은 유형의 고유 식별자로 constexprtype_info::hash_code을 추가하지만 결합하지는 않습니다. 형식 이름이나 정적 형식의 식에서 상수 식에 typeid을 사용할 수 있지만 hash_code은 비 constexpr 함수이므로 호출 할 수 없습니다.

물론 다양한 해결 방법이 있습니다. 그 중 하나는 설명하는 것이고 가장 일반적인 방법은 템플릿 메타 프로그래밍을 사용하여 유형 벡터를 통해 방문자를 적용하는 것입니다. 단지 몇 가지 유형이 유효하기 때문에

+0

그래서 나는'type_info :: hash_code'를 키로 사용하여'unordered_map '를 사용할 수 있었고 관련 캐스트를 수행하고'g'를 호출하는 람다를 사용할 수 있었다고 생각합니다. –

+0

@AndrewTomazosFathomlingCorps 그게 효과가있다. 템플릿 펑터를 사용하면 여러 개의 람다를 작성할 필요가 없습니다. 그러나'int' 대신'std :: size_t'를 사용하십시오. – Potatoswatter

+0

해결책이 아니기 때문에 Downvoting, 그리고 매우 오해의 소지가 있습니다 : "C++ 03에서는 불가능했습니다"는 "it"= 의도 된 효과를 가진 코드를 작성하기위한 쓰레기 일뿐입니다. 저의 대답에서 볼 수 있듯이, 여기까지 제시된 유일한 해결책은 쉽지 않습니다. 현재 SO에서 자주 발생하는 것처럼 부정 투표 집계표가 있습니다. –

3

, 당신은 가상 함수 대신 템플릿 특수화와 함께이 문제를 해결할 수 :

struct B 
{ 
    virtual void g() = 0; 
} 

template<typename T> 
struct D : public B 
{ 
    T t; 
}; 

template<> 
struct D<int> : public B 
{ 
    int t; 
    void g() { /* do something here */ } 
}; 

template<> 
struct D<std::string> : public B 
{ 
    std::string t; 
    void g() { /* do something here */ } 
}; 

template<> 
struct D<char> : public B 
{ 
    char t; 
    void g() { /* do something here */ } 
}; 

void f(B* b) 
{ 
    b->g(); 
} 

대신 또는 런타임 검사를 필요로하는, 잘못된 유형을 제공하는 경우는 컴파일시 실패합니다 (C++은 매우 나쁘다).

+0

실제 문제는 유효한 유형이 많습니다. –

+1

@AndrewTomazosFathomlingCorps 여전히 가능한 해결책이 될 수 있습니다. 클래스 정의를 돕기위한 전 처리기 매크로. –

-1

C++에서 런타임 전환 유형의 주된 선택은 가상 함수입니다.

그것은 죽은 간단하다 : 그것은 또한 효율적으로 할 수있는 한

#include <string> 
#include <iostream> 
using namespace std; 

struct Base 
{ 
    virtual void g() const = 0; 
}; 

template< class Type > void g(Type const&); 

template<> void g(int const&) { cout << "int" << endl; } 
template<> void g(string const&) { cout << "string" << endl; } 
template<> void g(char const&) { cout << "char" << endl; } 

template< class Type > 
struct Derived: Base 
{ 
    Type t; 
    virtual void g() const override { ::g<Type>(t); } 
}; 

void f(Base& b) { b.g(); } 

int main() 
{ 
    Derived<int>().g(); 
} 

, O (1) 대신 바보 O ( N). 또한 동적 (런타임) 유형 검사 대신 정적 (컴파일 타임) 유형 검사를 사용하여 매우 귀찮은 양의 테스트를 저장합니다. 나는 무엇을 더 말할 수 있냐? 정말로 타입 코드와 열거 형 등을 잊어 버려라. Bertrand Meyer가 으로 선택 했으므로 에펠의 enum을 지원하지 않기 때문에 사람들이 형식 코드를 남용하는 경향이 있습니다. 가상 기능을 사용하십시오.

안녕하세요, 가상 기능!

동적 인 디스패치를 ​​원할 경우 정말 유용합니다.

그래서 가상 함수를 사용하는 것이 좋습니다.:)


편집은 : 실제 코드에서 가능한 모호성을 피하기 위해 ::g을 플릿.

+0

익명 downvoter, 당신은 다른 사람에게 그것이 * 어느 * 사용자에게 그렇게 무능력하다는 것을 알려주지 않았습니다. 예, SO 시스템에있는 투표 시스템이 기술적 정확성을 향상시키지 않는다는 것을 독자들에게 알리고 있습니다. 투표가 완전히 낭비되지는 않았지만, 당신이 누구인지 아는 것이 더 낫습니다. –