42: typename의 두 가지 의미를 제대로 파악하자
template<class T> class Widget;
template<typename T> class Widget;
위의 두 템플릿 선언문에서 class와 typename의 차이점은 무엇일까? 정답은 차이가 없다. 템플릿 매개변수를 선언하는 경우의 class 및 typename은 완전히 같은 의미를 지닌다.
하지만 typename을 사용하지 않으면 안 되는 때가 있다. 이때가 언제인지를 알아보자. 일단 템플릿 안에서 참조할 수 있는 이름의 종류는 두 가지이다.
template<typename C>
void print2nd(const C& container)
{
if (container.size() >= 2)
{
// 제정신으로 짠 코드가 아니다!
C::const_iterator iter(container.begin());
++iter;
int value = *iter;
std::cout << value;
}
}
위에서 눈여겨 봐야할 것은 지역 변수인 iter과 value이다. iter의 타입은 C::const_iterator인데, 템플릿 매개변수인 C에 따라 달라지는 타입이다. 템플릿 내의 이름 중에 이렇게 템플릿 매개변수에 종속된 것을 가리켜 의존 이름(dependent name)이라고 한다. 의존 이름이 어떤 클래스 안에 중첩되어 있는 경우가 있는데, 이 책에서는 이 경우 중첩 의존 이름(nested dpendent name)이라고 부른다.
value는 int 타입이다. int는 템플릿 매개변수가 어떻든 상관없는 타입 이름이다. 이러한 이름은 비의존 이름(non-dependent name)이라고 한다.
코드 안에 의존 이름이 있으면 컴파일러가 구문분석을 할 때 골치 아픈 일이 발생할 수 있다.
template<typename C>
void print2nd(const C& container)
{
C::const_iterator * x;
}
C::const_iterator가 타입이라는 것은 우리는 알 수 있지만 컴파일러는 알 수 없다. 우연히 const_iterator라는 이름을 가진 정적 데이터 멤버가 C에 들어가 있다고도 볼 수 있다. 이런 경우라면 위의 코드는 지역 변수를 선언한 것이 아닌, 곱셈 연산을 수행할 것이다.
C의 정체가 무엇인지 알려 주지 않으면, C::const_iterator가 진짜 타입인지 아닌지를 알아낼 방법이 없다. C++는 이러한 모호성을 없애기 위해, 중첩 의존 이름을 기본적으로 타입이 아닌 것으로 해석한다.
이런 문제를 해결하려면 C::const_iterator 앞에 typename 키워드를 붙여야 한다.
template<typename C>
void print2nd(const C& container)
{
if (container.size() >= 2)
{
typename C::const_iterator iter(container.begin());
++iter;
int value = *iter;
std::cout << value;
}
}
typename 키워드는 중첩 의존 이름만 식별하는 데 써야 한다. 그 외의 이름은 type-name을 가져서는 안 된다.
template<typename C>
void f(const C& container, // typename 쓰면 안됨
typename C::iterator iter); // typename을 써야됨
"typename은 중첩 의존 타입 이름 앞에 붙여 주어야 한다"는 규칙에 예외가 하나 있다. 중첩 의존 타입 이름이 기본 클래스의 리스트에 있거나 멤버 초기화 리스트 내의 기본 클래스 식별자로서 있을 경우에는 typename을 붙여선 안 된다.
template<typename T>
class Derived : public Base<T>::Nested { // 상속되는 기본 클래스 리스트: typename 쓰면 안 됨
public:
explicit Derived(int x)
: Base<T>::Nested(x) // 멤버 초기화 리스트에 있는 기본 클래스 식별자: typename 쓰면 안 됨
{
typename Base<T>::Nested temp; // 중첩 의존 이름: typename 써야됨
}
};
이것만은 잊지 말자!
- 템플릿 매개변수를 선언할 때, class 및 typename은 서로 바꾸어 써도 무방하다.
- 중첩 의존 타입 이름을 식별하는 용도에는 반드시 typename을 사용한다. 단, 중첩 의존 이름이 기본 클래스 리스트에 있거나 멤버 초기화 리스트 내의 클래스 식별자로 있는 경우에는 예외이다.
'C++ > Effective C++' 카테고리의 다른 글
[Effective C++] 44: 매개변수에 독립적인 코드는 템플릿으로부터 분리시키자 (1) | 2023.11.13 |
---|---|
[Effective C++] 43: 템플릿으로 만들어진 기본 클래스 안의 이름에 접근하는 방법을 알아 두자 (0) | 2023.11.13 |
[Effective C++] 41: 템플릿 프로그래밍의 천릿길도 암시적 인터페이스와 컴파일 타임 다형성부터 (0) | 2023.11.10 |
[Effective C++] 40: 다중 상속은 심사숙고해서 사용하자 (2) | 2023.11.09 |
[Effective C++] 39: private 상속은 심사숙고해서 구사하자 (0) | 2023.11.07 |