[Effective C++] 42. typename의 두 가지 의미를 제대로 파악하자
C++에서 템플릿 매개변수의 경우 class와 typename은 완전히 같은 의미이다.
template<class T> class Widget;
template<typename T> class Widget;
의존이름(dependent name) vs 비의존 이름(non-dependent name)
template<typename C>
void print2nd(const C& container)
{
if (container.size() >= 2)
{
C::const_iterator iter(container.begin());
++iter;
int value = *iter;
cout << value << endl;
}
}
의존 이름
위 코드에서 iter 변수의 타입은 C::const_iterator인데, 템플릿 매개변수인 C에 따라 달라지는 타입이다.
즉, 템플릿 매개변수에 종속된 것을 의존이름이라고 한다.
그리고 의존이름이 어떤 클래스 안에 중첩되어 있는 경우가 있는데 이 경우의 이름을 중첩 의존 타입 이름이라고 한다.
비의존 이름
위 코드에서 value는 int타입이다. int는 기본 자료구조로써 템플릿 매개변수가 어떻든 상관없는 타입이다.
즉, 템플릿 매개변수에 상관없는 타입 이름을 비의존 이름이라고 한다.
중첩 의존 타입 이름
중첩 의존 타입이란 템플릿 안의 지역 변수의 타입이 템플릿 매개변수에 종속된 의존 이름일 때, 다른 클래스 안에 이름이 중첩되어 있는 경우를 말한다.
이렇게 코드내에 중첩 의존 타입 이름이 있으면 골치가 아픈데, 그 이유에 대해서 알아보자.
template<typename C>
void print2nd(const C& container)
{
C::const_iterator* x;
}
C::const_iterator에 대한 포인터 지역 변수로 x를 선언하고 있는 것으로 보인다. 하지만 만약 const_iterator라는 이름을 가진 정적 데이터 멤버가 C에 있다면 C::const_iterator와 x의 곱셈 연산이 될 수 있다.
컴파일러는 C의 정체가 무엇인지 다른 곳에서 알려주지 않으면 C::const_iterator가 진짜 타입인지 아닌지를 알아낼 방법이 없다. 이 때 C++은 모호성을 해결하기 위해 규칙하나를 사용하는데, 템플릿 안에서 중첩 의존 타입 이름을 만나면 개발자가 타입이라고 알려주지 않는 한, 타입이 아니라고 가정한다.
간단히 말해 중첩 의존 타입은 한가지 예외를 제외하고 기본적으로 타입이 아닌 것으로 해석한다.
이 한가지 예외는 typename 키워드를 붙여주는 것이다.
template<typename C>
void print2nd(const C& container)
{
typename C::const_iterator iter(container.begin());
}
결과적으로 typename 키워드는 중첩 의존 타입 이름만 식별하는데 써야한다.
하지만 typename 키워드를 사용하면 안되는 한가지 예외사항이 있는데, 중첩 의존 타입 이름이 기본 클래스의 리스트에 있거나 멤버 초기화 리스트 내의 기본 클래스 식별자로서 있는 경우에는 typename을 붙이면 안된다.
기본 클래스의 리스트라는건 상속할 때 클래스 선언 시를 의미하고, 멤버 초기화 리스트는 생성자에서 대입이 아닌 초기화 시를 말한다.
template<typename T>
class Derived : public Base<T>::Nested //상속되는 기본 클래스 리스트. typename 쓰면 안됨
{
public:
explicit Erived(int x)
: Base<T>::Nested(x) //멤버 초기화 리스트에 있는 기본 클래스 식별자. typename 쓰면 안됨
{
typename Base<T>::Nested temp; //중첩 의존 타입 이름이므로 typename 필요
}
};
요약
- 템플릿 매개변수를 선언할 때, class 및 typename은 서로 바꾸어 써도 무방하다.
- 중첩 의존 타입 이름을 식별하는 용도에는 반드시 typename을 사용한다. 단, 중첩 의존 이름이 기본 클래스 리스트에 있거나 멤버 초기화 리스트 내의 기본 클래스 식별자로 있는 경우에는 예외이다.
'Books > Effective C++' 카테고리의 다른 글
[Effective C++] 46. 타입 변환이 바람직할 경우에는 비멤버 함수를 클래스 템플릿 안에 정의해 두자 (0) | 2022.07.15 |
---|---|
[Effective C++] 43. 템플릿으로 만들어진 기본 클래스 안의 이름에 접근하는 방법을 알아 두자 (0) | 2022.07.08 |
[Effective C++] 41. 템플릿 프로그래밍의 천릿길도 암시적 인터페이스와 컴파일 타입 다형성부터 (0) | 2022.07.08 |
[Effective C++] 40. 다중 상속은 심사숙고해서 사용하자 (0) | 2022.07.03 |
[Effective C++] 39. private 상속은 심사숙고해서 구사하자 (0) | 2022.07.03 |
댓글
이 글 공유하기
다른 글
-
[Effective C++] 46. 타입 변환이 바람직할 경우에는 비멤버 함수를 클래스 템플릿 안에 정의해 두자
[Effective C++] 46. 타입 변환이 바람직할 경우에는 비멤버 함수를 클래스 템플릿 안에 정의해 두자
2022.07.15 -
[Effective C++] 43. 템플릿으로 만들어진 기본 클래스 안의 이름에 접근하는 방법을 알아 두자
[Effective C++] 43. 템플릿으로 만들어진 기본 클래스 안의 이름에 접근하는 방법을 알아 두자
2022.07.08 -
[Effective C++] 41. 템플릿 프로그래밍의 천릿길도 암시적 인터페이스와 컴파일 타입 다형성부터
[Effective C++] 41. 템플릿 프로그래밍의 천릿길도 암시적 인터페이스와 컴파일 타입 다형성부터
2022.07.08 -
[Effective C++] 40. 다중 상속은 심사숙고해서 사용하자
[Effective C++] 40. 다중 상속은 심사숙고해서 사용하자
2022.07.03