항목 42. typename의 두 가지 의미를 제대로 파악하자
1. class와 typename 공통점
템플릿 매개변수 선언시 class와 typename은 완전히 동일하다.
template<class T> class Widget; // class 사용
template<typename T> class Widget; /typename 사용
2. class와 typename 차이점
하지만 class 와 typename 키워드가 다르게 사용되는 경우가 있다.
template<typename C>
void print2nd(const C& container) //컨테이너에 들어 있는 2번째 원소를 출력합니다.
{
if (container.size() >= 2 ) {
C::const_iterator iter(container.begin()); //첫째 원소에 대한 반복자를 얻습니다.
++iter; // 두번째원소로 iter를 옮깁니다.
int value = *iter; // value에 값을 복사합니다.
std::cout << value; // value을 출력합니다.
}
}
* 풀이
1) 템플릿 내의 이름 중(C::const_iterator iter)에 위와같이 템플릿 매개변수에 종속된 것을 가리켜 의존 이름(dependent name)이라고 한다. 클래스 안에 중첩되어 있는 경우가 있는데, 이 경우의 이름은 중첩 의존 이름(nested dependent name)이라고 부른다.
2) 또다른 지역변수, value는 템플릿 매개변수와 상관없는 타임이름이므로, 비의존 이름(non-dependent)이라고 한다.
3) 중첩 의존 이름의 올바른 사용방법
- 중첩 의존이 코드안에 있을때 에러를 꽃피울 수 있다.
template<typename C>
void print2nd(const C& container)
{
C::const_iterator * x;
…
}
*풀이 : 우리는 C::const_iterator에 대한 포인터인 지역변수로서 x를 선언하고 있는 것으로 생각하겠지만, 만약 C가 const_iterator라는 정적데이터멤버를 갖고 있고, x가 전역변수라면, 컴파일러는 두 데이터를 곱셈연산한다고 생각할 것이다.
따라서, 바로 이경우에 C::const_iterator 앞에 typename이라는 키워드를 명시해준다. (데이터가 아니고, 타입이라는 걸 명시하기위해)
4. typename의 예외
- "typename은 중첩의존이름앞에 꼭 붙여주여야 한다"라는 규칙에 예외가 2가지가 있다.
1) 상속되는 기본클래스의 리스트에 있을때
2) 초기화 리스트 내에 기본클래스 식별자 일때
template<typename T>
class Derived: public Base<T>::Nested { //상속되는 기본클래스 리스트, typename X
public :
explicit Derived(int x) : Base<T>::Nested(x) { //초기화리스트내에 기본클래스 식별자, typename X
typename Base<T>::Nested temp; // 중첩의존타입의 이름, typename O
…
}
…
};
이것만은 알아두자
- 템플릿 매개변수를 선언할 때, class 및 typename은 서로 바꾸어 써도 무방합니다.
- 중첩의존타입이름을 식별하는 용도에는 반드시 typename을 사용해야한다. 단, 중첩의존이름이 기본클래스 리스트 or 멤버 초기화 리스트내에 기본 클래스 식별자로 있을 경우는 예외이다.