Post List

2015년 1월 4일 일요일

typename의 두 가지 의미를 제대로 파악하자.

template<class T> class Widget;
template<typename T> class Widget;

위의 선언문에서 class와 typename의 차이가 뭘까 ? 없다. 같은 뜻이다. 하지만 typename을 쓰지 않으면 안 되는 때가 분명히 있다.

 템플릿 안에서 참조할 수 있는 이름에는 두 가지 종류가 있다. 하나는 템플릿 매개변수에 종속되는 중첩 의존 타입 이름(nested dependent type name) 이고 다른하나는 int와 같이 템플릿 매개변수와는 상관없는 비의존 이름(non-dependent name) 이다.

template<typename C>
void func(const C& container)
{
  C::const_iterator * x;
}




 위 Code에서 C::const_iterator가 타입이 아니라 C의 멤버 데이터라면 어쩔것인가 ? 그리고 x가 전역 변수라면 저건 두 변수의 곱인가 ? 말도 안되는 상상이라고 생각할 수 있지만 충분히 가능한 일이다. C++는 이런 모호성을 해결하기 위해 규칙을 하나 정했다. 템플릿 안에 충첩 의존 이름을 만나면 프로그래머가 타입이라고 알려주지 않는 이상 무조건 타입이 아닌 것으로 취급한다. 그러니 이 난국을 바로 잡을 방법은 하나 밖에 없다. 앞에다가 typename 키워드를 써주는 것이다.

template<typename C>
void func(const C& container)
{
  typename C::const_iterator * x;
}

 중첩 의존 타입 이름 앞에 typename을 붙이면 안되는 예외가 하나 있다. 기본 클래스의 리스트에 있거나 멤버 초기화 리스트 내의 기본 클래스 식별자로 있는 경우에는 typename을 붙여주면 안된다. 무슨 말인지 모르겠다고 ? 아래 Code를 보자.

template<typename T>
class Derived : public Base<T>::Nested { // 상속되는 기본 클래스 리스트
public:
  explicit Derived(int x)
    : Base<T>::Nested(x) // 멤버 초기화 리스트에 있는 기본 클래스 식별자
  {
    typename Base<T>::Nested temp; // typename 꼭 붙여야 함
  }
};

 현업에서 자주 사용하는 Code 중 반복자를 매개변수로 받는 어떤 함수 템플릿이 있는데, 매개변수로 넘어온 반복자가 가리키는 객체의 사본을 만드는 경우가 있다. 객체로 부터 그 Type을 추출해 내는 과정으로 C# 과 Java의 Reflect를 사용하는 것과 비슷한 경우이다. 이럴 경우 typename이 여러번 쓰인다면 무지막지하게 길게 되어서 typedef를 사용하는 것이 효과적인 경우가 많다. 보통 그럴경우 그 멤버 이름과 똑같은 이름으로 짓는 것이 관례로 되어 있다.

template<typename T>
void workWithIterator<T iter> {
  typedef typename std::iterator_traits<T>::value_type value_type;
  value_type temp(*iter);
}

 * 템플릿 매개변수를 선언할 때, class 및 typename은 서로 바꾸어 써도 무방합니다.

 * 중첩 의존 타입 이름을 식별하는 용도에는 반드시 typename을 사용합니다. 단, 중첩 의존 이름이 기본 클래스 리스트에 있거나 멤버 초기화 리스트 내의 기본 클래스 식별자로 있는 경우에는 예외입니다.

댓글 없음:

댓글 쓰기