항목 42 : typename의 두 가지 의미를 제대로 파악하자
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;
}
}
Colored by Color Scripter
|
템플릿 내의 이름 중에 템플릿 매개변수에 종속된 것을 가리켜 의존 이름이라고 합니다. 의존 이름이 어떤 클래스 안에 중첩되어 있는 경우가 있는데, 이 경우 이름을 중첩 의존 이름이라고 부릅니다. 위의 코드에서 C::const_iterator는 중첩 의존 이름입니다. 정확히 말하자면 중첩 의존 타입 이름이라고 해야 맞습니다. 타입을 참조하는 중첩 의존 이름이란 뜻이지요.
print2nd 함수에서 쓰이는 또 하나의 지역 변수, value는 int 타입입니다. int는 템플릿 매개변수가 어떻든 상관없는 타입 이름입니다. 이러한 이름은 비의존 이름이라고 합니다.
구문분석기는 템플릿 안에서 중첩 의존 이름을 만나면 프로그래머가 타입이라고 알려 주지 않는 한 그 이름이 타입이 아니라고 사정하게 되어 있습니다. 다시 말해, 중첩 의존 이름은 기본적으로 타입이 아닌 것으로 해석됩니다.
1
2
3
4
5
6
7
8
9
10
|
template<typename C>
void print2nd(const C& container)
{
if(container.size() >= 2)
{
C::const_iterator iter(container.begin()); // 이 름은 타입이 아닌 것으로 가정합니다.
...
}
}
Colored by Color Scripter
|
iter의 선언이 선언으로서 의미가 있으려면 C::const_iterator가 반드시 타입이어야 하는데, 프로그래머가 C++ 컴파일러에게 타입이라고 알려 주지 않았으니, C++는 제멋대로 타입이 아닌 것으로 가정해 버린 것입니다. 이것을 해결하는 방법은 C++에게 C::const_iterator가 타입이라고 말해 주는 것입니다. C::const_iterator 앞에다가 typename이라는 키워드를 붙여 놓습니다.
1
2
3
4
5
6
7
8
9
10
|
template<typename C>
void print2nd(const C& container)
{
if(container.size() >= 2)
{
typename C::const_iterator iter(container.begin());
...
}
}
Colored by Color Scripter
|
어느 때이든지 템플릿 안에서 중첩 의존 이름을 참조할 경우에는, 그 이름 앞에 typename 키워드를 붙여 주는 것을 잊지 마세요.
"typename은 중첩 의존 타입 이름 앞에 붙여 주어야 한다"는 규칙에 예외가 하나 있습니다.
중첩 의존 타입 이름이 기본 클래스의 리스트에 있거나 멤버 초기화 리스트 내의 기본 클래스 식별자로서 있을 경우에는 typename 붙여 주면 안 된다는 것입니다.
1
2
3
4
5
6
7
8
9
10
|
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
...
}
...
};
Colored by Color Scripter
|
typename에 관한 예제를 하나 더 보도록 하겠습니다.
1
2
3
4
5
6
|
template<typename IterT>
void workWithIterator(IterT iter)
{
typename std::iterator_traits<IterT>::value_type temp(*iter);
...
}
Colored by Color Scripter
|
std::iterator_traits<IterT>::value_type은 중첩 의존 타입 이름이므로, 이 이름 앞에는 typename을 써 주어야 합니다.
특성정보 클래스에 속한 value_type 등의 멤버 이름에 대해 typedef 이름을 만들 때는 그 멤버 이름과 똑같이 짓는 것이 관례로 되어 있습니다.
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);
...
}
Colored by Color Scripter
|
꼭 잊지 말아야 할 것!
1. 템플릿 매개변수를 선언할 때, class 및 typename은 서로 바꾸어 써도 무방합니다.
2. 중첩 의존 타입 이름을 식별하는 용도에는 반드시 typename을 사용합니다. 단, 중첩 의존 이름이 기본 클래스 리스트에 있거나 멤버 초기화 리스트 내의 기본 클래스 식별자로 있는 경우에는 예외입니다.
'언어 > C++' 카테고리의 다른 글
[Effective C++] 템플릿으로 만들어진 기본 클래스 안의 이름에 접근하는 방법을 알아 두자 (0) | 2020.02.12 |
---|---|
[Effective C++] 템플릿 프로그래밍의 천릿길도 암시적 인터페이스와 컴파일 타임 다형성부터 (0) | 2020.02.10 |
[Effective C++] 다중 상속은 심사숙고해서 사용하자 (0) | 2020.02.09 |
[Effective C++] private 상속은 심사숙고해서 구사하자 (0) | 2020.02.08 |
[Effective C++] "has-a(...는...를 가짐)" 혹은 "is-implemented-in-terms-of(...는...를 써서 구현됨)"를 모형화할 때는 객체 합성을 사용하자 (0) | 2020.02.07 |