std::is_same 가 제대로 작동하지 않는것 같습니다.
조회수 2953회
안녕하세요 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
가 가능합니다.
댓글 입력