std::is_same 가 제대로 작동하지 않는것 같습니다.

조회수 2921회

안녕하세요 c++, stl 코드 작성에 약간의 에러가 발생하여 질문드립니다. 먼저 에러가 나는 코드는 아래와 같습니다. 주석 처리한 곳에서 에러가 납니다.

template<typename T>
void D::Store() {
    // remove all type cvr.
    using originalType = std::decay_t<std::remove_pointer_t<T> >;
    originalType* component = new originalType();

    if(std::is_same<originalType, A>::value) { // 에러!!
        this->mA = component;
    } else if(std::is_same<originalType, B>::value) { // 에러!!
        this->mB = component;
    } else if(std::is_same<originalType, C>::value) { // 에러!!
        this->mC = component;
    }
    mComponents.push_back(component);
}

A,B,C 라는 타입이 미리 정의가 되어있고 그 타입에 해당하는 포인터가 클래스에 멤버로 정의되어 있습니다.

class D {
    D();
    ~D();

    template<typename T>
    void Store();

private:
    A* mA;
    B* mB;
    C* mC;
    std::vector<Parent*> mComponents;
}

사용 용도는 만약 store 로 넘기는 타입 T 가 A,B,C타입중 하나라면 해당하는 멤버의 포인터에 저장후 vector자료형에 캐싱하는 용도입니다. 물론 A,B,C타입은 전부 Parent를 상속받고 있구요. 그 외에도 클래스 타입 E,F ... 등등 Parent를 상속받는 여러 클래스들이 있습니다.

그런데 std::is_same하는 부분에서 에러가 나옵니다. 에러 메세지는 Assigning to 'A*' from incompatible type 'originalType*' (aka 'E*") 이런 에러가 뜹니다.

제가 원하는것은 딱 A,B,C타입이 주어졌을 때만 멤버 변수에 저장하고 싶은 것인데 어떻게 해야 제대로 작동하게 할 수 있을까요?

참고로 개발 환경은 xcode 7.3.1 버전 이구요, os 는 os x el capitan 입니다.

  • (•́ ✖ •̀)
    알 수 없는 사용자

2 답변

  • 템플릿이 특수화될 때, 컴파일러는 특수화된 버전을 컴파일하려고 시도합니다. 예를 들어서, Store<A>를 특수화할 때를 생각해 봅시다.

    template<>
    void D::Store<A>() {
        A* component = new A(); // 뭐 정확히 이건 아니지만.. 중요한건 아니니까요.
    
        if(std::is_same<A, A>::value) {
            this->mA = component;
        } else if(std::is_same<A, B>::value) {
            this->mB = component; // Error
        } else if(std::is_same<A, C>::value) {
            this->mC = component; // Error
        }
        mComponents.push_back(component);
    }
    

    설령 if문의 조건부가 false라고는 해도, 어쨋건 코드는 적법한 C++ 코드여야 합니다. this->mB (혹은 mC)와 component의 타입 불일치로 인해 오류가 나죠. Cygwin GCC 5.4.0에서는 이런 에러 메시지를 보여주네요.

    test.cpp: In instantiation of ‘void D::Store() [with T = A]’:
    test.cpp:53:15:   required from here
    test.cpp:35:18: error: cannot convert ‘originalType* {aka A*}’ to ‘B*’ in assignment
             this->mB = component;
    
    

    이런 문제 때문에 템플릿 메타프로그래밍을 할 때에는 단순히 if를 사용하지 않습니다. 대신 if의 역할을 하는 것이 몇몇 있는데, 이 경우에는 tag dispatch라는 기법이 적합해 보입니다.

    struct tag_none { };
    struct tag_A { };
    struct tag_B { };
    struct tag_C { };
    
    template <typename T>
    struct component_tag { using type = tag_none; };
    template <>
    struct component_tag <A> { using type = tag_A; };
    template <>
    struct component_tag <B> { using type = tag_B; };
    template <>
    struct component_tag <C> { using type = tag_C; };
    
    class D
    {
    public:
        template <typename T>
        void Store();
    
    private:
        void Store_impl(void*, tag_none);
        void Store_impl(A* component, tag_A);
        void Store_impl(B* component, tag_B);
        void Store_impl(C* component, tag_C);
    
        A *mA;
        B *mB;
        C *mC;
        std::vector<Parent*> mComponents;
    };
    
    template <typename T>
    void D::Store()
    {
        using originalType = std::decay_t<std::remove_pointer_t<T> >;
        originalType* component = new originalType();
    
        Store_impl(component, typename component_tag<originalType>::type { });
    
        mComponents.push_back(component);
    }
    
    void D::Store_impl(void*, tag_none)
    {
        // do nothing
    }
    
    void D::Store_impl(A* component, tag_A)
    {
        mA = component;
    }
    
    void D::Store_impl(B* component, tag_B)
    {
        mB = component;
    }
    
    void D::Store_impl(C* component, tag_C)
    {
        mC = component;
    }
    

    코드에서 보시다시피, tag라는 여분의 인수를 통해 함수 오버로딩을 하는 것이죠. 여기 실제로 컴파일되는 예제입니다.

    • (•́ ✖ •̀)
      알 수 없는 사용자
  • 트릭에 가까운 방법인데요. dynamic_cast를 써보시면 어떨까합니다.

    void D::Store(Parent *t) {
        if(dynamic_cast<A*>(t)) {
            this->mA = (A*)t;
        } else if (dynamic_cast<B*>(t)) { 
            this->mB = (B*)t;
        } else if (dynamic_cast<C*>(t)) {
            this->mC = (C*)t;
        }
        mComponents.push_back(t);
    }
    

    Parent의 소멸자가 virtual로 선언되어야 dynamic_cast가 가능합니다.

    • 함수 설계상 인자로 받는게 아니라 템플릿 으로 받는 구조라서 이방법은 힘들것 같네요 알 수 없는 사용자 2016.8.29 11:52

답변을 하려면 로그인이 필요합니다.

프로그래머스 커뮤니티는 개발자들을 위한 Q&A 서비스입니다. 로그인해야 답변을 작성하실 수 있습니다.

(ಠ_ಠ)
(ಠ‿ಠ)