1탄은 traits 를 참고.
Traits의 가장 큰 특성은, non-intrusive하다는 점입니다. 즉, 어떤 타입 정보를 전달함에 있어서 기존의 코드를 최소한으로 깬다는 것이죠. 예를들어 A라는 클래스는 tag1의 속성을, B라는 클래스는 tag2의 속성을 갖는다고 합시다. 그러면 다음과 같이 tag를 정의할 수 있을 것입니다.
struct tag1 { }; struct tag2 { }; ... struct A { typedef tag1 type; }; struct B { typedef tag2 type; };
그러면 우리는 A의 속성은 A::type으로, B의 속성은 B::type으로 접근가능하겠죠. 이런 것이 왜 필요하냐고 의문을 품을 수 있겠지만, 이런 것이 필요한 경우는 많습니다. 한 예가 iter_swap example인데요. 해당 예에서는 vector에 저장된 타입이 int일까? string일까? 같은 것을 결정하는데 있어 다음과 같은 식의 코드를 씁니다.
vector<string> vs; iterator_traits<vs>::value_type
그럼 필요성은 확인이 된거니, 다시 앞의 예로 돌아가죠. 우리는 A와 B의 타입 정보를 이제 압니다. 그런데 이 시스템에 우리가 못건드는 C라는 클래스나 혹은 ‘int’ 라는 primitive 에 대해서 속성을 정하고 싶다면 무척 난감하겠죠. 이 경우에 traits를 사용해 해결할 수 있습니다. 먼저, 일반적인 용도의 traits를 정의합니다.
template <class T> struct traits { typedef typename T::type type; };
이제 속성 정보가 필요하다면 traits<A>::type 이나 traits<B>::type 과 같이 접근할 수 있습니다. 다음, 우리가 손을 못대는 경우에는 다음과 같이 specialization시킵니다.
template <> struct traits<int> { typedef tag1 type; };
멋지죠? 그러면 이제 int 에 대해서는 traits<int>::type 로 접근하면되고, 이것은 A, B, int에 대해 동일한 인터페이스가 가능함을 뜻합니다. 전체를 포괄하는 예를 보죠.
#include
using namespace std;
struct tag1 { };
struct tag2 { };
ostream& operator<<(ostream& os, const tag1&)
{
os << "tag1" << endl;
return os;
}
ostream& operator<<(ostream& os, tag2)
{
os << "tag2" << endl;
return os;
}
struct A
{
typedef tag1 type;
};
struct B
{
typedef tag2 type;
};
template
struct traits
{
typedef typename T::type type;
};
template <>
struct traits
{
typedef tag1 type;
};
int main()
{
cout << "traits::type=” << traits::type() << endl;
cout << "traits::type=” << traits::type() << endl;
cout << "traits
따라서 해당 객체들은 const T& 로 받거나, T로 값에 의한 복사를 해야합니다. 이 때, 한가지 또 궁금증은 왜 T&는 안되고, const T&는 될까겠죠. 그 이유는, 스택에 만들었다가 곧 파괴할 객체는 참조로 받아서 값을 변형하는 것은 무의미하기 때문에 참조로 받는 것을 금하고, const 의 경우엔 참조로 받더라도 수정이 불가하므로 허가한다고 생각하면 됩니다.