일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- 반복자
- Effective c++
- 비교 함수 객체
- 게임
- Smart Pointer
- 영화 리뷰
- 메타테이블
- 오블완
- more effective c++
- UE4
- resource management class
- c++
- 루아
- 예외
- implicit conversion
- 언리얼
- 스마트 포인터
- 티스토리챌린지
- lua
- 영화
- effective stl
- 암시적 변환
- virtual function
- exception
- Vector
- 참조자
- 다형성
- 상속
- reference
- operator new
Archives
- Today
- Total
스토리텔링 개발자
[Effective C++] 42. typename의 두 가지 용법 본문
728x90
항목 42 : typename의 두 가지 의미를 제대로 파악하자
템플릿에서 class와 typename의 차이
template<class T> class Widget; // class 키워드를 사용한다.
template<typename T> class Widget; // typename 키워드를 사용한다.
- 이 경우 이 둘은 사실상 완전히 같은 의미를 가진다.
- 하지만 typename을 쓰지 않으면 안 되는 경우가 있다.
템플릿 안에서 참조할 수 있는 두 종류의 이름
// 컨테이너의 두 번째 원소를 출력한다.
// 하지만 문제가 있는 코드!!
tmeplate<typename C>
void print2nd(const C& container)
{
if(container.size() >= 2)
{
C::const_iterator iter(container.begin()); // iter : 의존 이름
++iter;
int value = *iter; // value : 비의존 이름
std::cout << value;
}
}
- 의존 이름(dependent name)
- 템플릿 매개변수에 종속적이다.
- 의존 이름이 어떤 클래스 안에 중첩되어 있는 경우엔 중첩 의존 이름(nested dependent name)이라고 부른다.
- 위의 경우 C::const_iterator는 중첩 의존 이름이다.
- C가 의존 이름이고, const_iterator 역시 의존 이름이기 때문이다.
- 비의존 이름(non-dependent name)
- 템플릿 매개변수와 관계 없는 타입 이름
중첩 의존 이름의 모호함
template<typename C>
void print2nd(const C& container)
{
// C::const_iterator가 타입이라 가정하지만...
// C::const_iterator 라는 정적 데이터 멤버가 존재한다면?
// 정적 데이터 멤버와 x를 피연산자로 한 곱셈 연산으로 인식할 것이다.
C::const_iterator* x;
...
}
- C의 정체가 무엇인지 다른 곳에서 알려주지 않으면, C::const_iterator가 진짜 타입인지 아닌지 컴파일러가 알아낼 방법은 없다.
- 이 경우 C++는 모호성을 해결하기 위해 아래 규칙에 따라 행동한다.
- 구문 분석기는 템플릿 안에서 중첩 의존 이름을 만나면 프로그래머가 타입이라고 알려주지 않는 한 그 이름이 타입이 아닌 것으로 해석한다.
- 다시 말해, 중첩 의존 이름은 기본적으로 타입이 아닌 것으로 해석된다.
template<typename C> void print2nd(const C& container) { if(container.size() >= 2) { // C::const_iterator를 타입이 아닌 것으로 가정한다!!!! C::const_iterator iter(container.begin()); ... } }
중첩 의존 이름이 타입임을 명시하기
template<taypename C>
void print2nd(const C& container)
{
if(container.size() >= 2)
{
// 이제 컴파일러는 C::const_iterator를 정상적으로 타입으로 이해한다.
typename C::const_iterator iter(container.begin());
...
}
}
- typename 키워드를 사용하여 타입임을 명시한다.
- 하지만, typename 키워드는 중첩 의존 이름을 식별할때만 써야 한다.
template<typename C> // typename을 쓸 수 있다.(class를 써도 된다.) void f(const C& container, // typename 쓰면 안된다. typename C::iterator iter); // typename 써야 한다.
- C는 중첩 의존 타입 이름이 아니기 때문에 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 필요
}
};
typename에 대한 또 다른 예
tmpelate<typename IterT>
void workWithIterator(IterT iter)
{
// 매개변수로 넘어온 반복자가 가리키는 객체의 사본을 복사해준다.
// 이 경우, std::iterator_traits<IterT>::value_type은 중첩 의존 타입이다.
// value_type이 iterator_traits<IterT> 안에 중첩되어 있고,
// IterT는 템플릿 매개변수이므로.
typename std::iterator_traits<IterT>::value_type temp(*iter);
...
}
- std::iterator_traits<IterT>::value_type에 관한 것은 항목 47 참조
- 여기서 std::iterator_traits<IterT>::value_type이 너무 기니까 typedef를 해보면..
template<typename IterT>
void workWithIterator(IterT iter)
{
typedef typename std::iterator_traits<IterT>::value_type value_type;
// 반복자가 가리키는 객체의 사본 생성
value_type temp(*iter);
...
}
- typedef typename 이 뭔가 잘못된 것 같아 보여도, 합법한 문법이다.
주의점
- typename으로 타입을 명시하지 않았을 때 컴파일 에러 여부는 컴파일러 의존적일 수 있다.
- 즉, typename을 붙이지 않고도 에러 없이 넘어갈 수 있으나, 다른 컴파일러에선 그렇지 않을 수 있다.
728x90
'개발 > Effective C++' 카테고리의 다른 글
[Effective C++] 44. 템플릿 코드 비대화 회피하기 (0) | 2024.07.16 |
---|---|
[Effective C++] 43. 템플릿 부모 클래스의 인터페이스에 접근하기 (0) | 2024.07.15 |
[Effective C++] 41. 템플릿 프로그래밍 (0) | 2024.07.11 |
[Effective C++] 40. 다중 상속 (0) | 2024.07.10 |
[Effective C++] 39. private 상속 (0) | 2024.07.09 |
Comments