[Effective C++] 42. typename의 두 가지 의미를 제대로 파악하자.
Question. 아래 두 템플릿 선언문에 쓰인 class와 typename의 차이점이 무엇일까요?
1
2
template<class T> class Widget; // class를 사용합니다.
template<typename T> class Widget; // typename을 사용합니다.
Answer. 차이가 없습니다. C++의 관점에서 보면, 템플릿 매개변수를 선언하는 경우의 class 및 typename은 완전히 같은 의미를 지닙니다.
그렇다고 언제까지나 class와 typename이 C++ 앞에서 동등d한 것만은 아닙니다. 먼저 템플릿에서 참고할 수 있는 이름의 종류가 두 가지라는 것부터 짚고 넘어가도록 하죠.
아래와 같은 함수 템플렛이 있다고 가정합시다. (참고로 아래 코드는 컴파일 되지 않습니다.)
1
2
3
4
5
6
7
8
9
10
11
12
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;
}
}
위의 코드에서 쓰이는 지역 변수 2개가 있습니다. 하나는 iter, 또 하나는 value 입니다. 템플릿 내의 이름 중에 이렇게 템플릿 매개변수에 종속된 것을 가리켜 의존 이름(dependent name)이라고 합니다. 그리고 의존 이름이 어떤 클래스 안에 중첩되어 있는 경우가 있는데, 이 경우의 이름을 중첩 의존 이름이라고 부릅니다. 위의 코드에서 C::Const_iterator은 중첩 의존 이름입니다. 사실 더 정확하게 말하자면 중첩 의존 타입 이름이라고 말해야 맞습니다.
참고로, print2nd의 또 하나의 지역 변수 int value는 비의존 이름(non-dependent name)이라고 합니다. 자 이제 용어에 대한 설명은 끝났고 본론으로 들어가 보도록 하죠. 코드 안에 중첩 의존 이름이 있으면 골치 아픈 일이 생깁니다. 바로 컴파일러가 구문분석을 할 때 중첩 의존 이름을 type이 아닌 변수로 본다는 것 입니다. 즉 아래의 코드에서 C::const_iterator * x를 포인터형 변수 선언이 아니라 변수의 곱을 실행하는 구문으로 본다는 것이지요.
1
2
3
4
5
6
template<typename C>
void print2nd(const C& container)
{
C::const_iterator * x;
...
}
다시 말해, 중첩 의존 이름은 기본적으로 타입이 아닌 것으로 해석 됩니다. 그렇다면 이런 경우에는 어떻게 해야 컴파일러가 타입으로 해석하도록 할 수 있을까요?
중첩 의존 이름을 type으로 구문해석 시키는 방법
중첩 의존 이름을 type으로 구문해석 시키는 두가지 방법이 있습니다.
- 초기화 리스트 내에 기본 클래스 식별자로서 위치시키는 방법 (이 경우 typename을 붙이면 안됨)
- typename 키워드를 사용하는 방법
1
2
3
4
5
6
7
8
9
10
11
12
template<typename T>
class Derived : public Base<T>::Nested
{
public:
explicit Derived(int x)
: Base<T>::Nested(x)
{
typename Base<T>::Nested temp;
...
}
...
};
참고로, 두번째 방법에 따라 작성한 아래의 코드가 약간 어색할 수도 있겠지만, 문법상으로 아무런 하자가 없는 코드라는 점도 짚고 넘어가도록 합시다.
1
2
3
4
5
6
7
template<typename IterT>
void workWithIterator(IterT iter)
{
typedef typename std::iterator_traits<IterT>::value_type value_type;
value_type temp(*iter);
...
}
End Note
- 템플릿 매개변수를 선언할 때, class 및 typename은 서로 바꾸어 써도 무방합니다.
- 중첩 의존 타입 이름을 식별하는 용도에는 반드시 typename을 사용합니다. 단, 중첩 의존 이름이 기본 클래스 리스트에 있거나 멤버 초기화 리스트 내의 기본 클래스 식별자로 있는 경우에는 예외 입니다.
Reference
- Effective C++ (Scott Meyers)
Leave a comment