C++ 템플릿 remove_pointer, enable_if, is_pointer 사용 예제
처음 해결하고 싶었던 문제는 다음과 같은 식의 코드였습니다.
#include "stdafx.h"
using namespace std;
#include <map>
struct MyType { MyType() { } };
template <typename _Key, typename _Value>
class MapTS
{
public:
MapTS()
{
}
void SetNew(_Key key)
{
_Value v = new _Value();
m_map.insert(pair<_Key, _Value>(key, v));
}
private:
map<_Key, _Value> m_map;
};
int main()
{
MapTS<int, MyType *> m2;
m2.SetNew(6);
return 0;
}
위의 코드를 컴파일하면 다음과 같은 오류가 발생합니다.
Error C2440 'initializing': cannot convert from 'MyType **' to 'MyType *'
당연합니다. "_Value v = new _Value();" 코드가 다음과 같이 바뀌기 때문입니다.
MyType *v = new MyType *();
이를 해결하기 위해서는 포인터를 하나 제거한 타입을 구해야 하는데요. 이때 사용할 수 있는 것이 바로 remove_pointer 구문입니다. 따라서, 다음과 같이 해주면 해결할 수 있습니다.
template <typename _Key, typename _Value>
class MapTS
{
typedef typename remove_pointer<_Value>::type _StructValue;
public:
MapTS()
{
}
void SetNew(_Key key)
{
_StructValue *v = new _StructValue();
m_map.insert(pair<_Key, _Value>(key, v));
}
private:
map<_Key, _Value> m_map;
};
그런데, 동일한 코드로 포인터 없는 형태로도 사용하고 싶어졌습니다.
MapTS<int, MyType> m1;
m1.SetNew(5);
이렇게 사용하면 다음과 같은 오류가 발생하게 됩니다.
Error C2440 '<function-style-cast>': cannot convert from 'initializer list' to 'std::pair<_Key,_Value>'
역시 당연합니다. "MyType"을 주었기 때문에 m_map 변수의 타입이 "map<int, MyType>"으로 바뀌었고 SetNew 함수에서 포인터를 밀어넣으려니 오류가 발생하는 것입니다. 따라서, 포인터 없는 구문이 동작하려면 SetNew 함수가 다음과 같이 바뀌어야 합니다.
void SetNew(_Key key)
{
_Value v = _Value();
m_map.insert(pair<_Key, _Value>(key, v));
}
이를 해결하고 싶다면, 포인터 있는 경우와 없는 경우를 구분하고 그에 따른 템플릿 특수화를 해야 합니다. 이를 위해 is_pointer와 enable_if를 다음과 같이 적용해 줄 수 있습니다. (enable_if의 존재를 회사 동료가 알려줬는데... 전혀 예상치 못한 확장이었습니다. ^^;)
template <typename _Key, typename _Value>
class MapTS
{
typedef typename remove_pointer<_Value>::type _StructValue;
public:
MapTS() { }
template<typename T = _Value>
void SetNew(_Key key, typename enable_if< is_pointer<T>::value, T >::type value = nullptr)
{
_StructValue *v = new _StructValue();
m_map.insert(pair<_Key, _Value>(key, v));
}
template<typename T = _Value>
void SetNew(_Key key, typename enable_if< !is_pointer<T>::value, T >::type value = _Value())
{
m_map.insert(pair<_Key, _Value>(key, _Value()));
}
private:
map<_Key, _Value> m_map;
};
int main()
{
MapTS<int, MyType> m1;
m1.SetNew(5);
MapTS<int, MyType *> m2;
m2.SetNew(6);
return 0;
}
(
첨부 파일은 이 글의 예제 코드를 포함합니다.)
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]