템플릿은 컴파일 타임에 인스턴스화 된다. 따라서 다양한 템플릿클래스를 처리하는 함수를 만들때 우리는 특성정보 클래스를 이용하여 컴파일 타임에 해당 클래스가 어떤 타입인지 밝혀낸다.
STL에는 컨테이너, 반복자, 알고리즘으로 구성되어있지만, 이 외에 유틸리티라고 불리는 템플릿도 들어 있다. 이들 중 하나가 advance라는 템플릿인데, 이 템플릿의 역할은 반복자를 지정된 거리만 큼 이동시키는 것이다.
template<typename IterT, typename DistT>
void advance(IterT& iter, DistT dist)
{
// ...
}
1. 반복자의 종류
advance를 살펴보기 전에 반복자의 종류5가지를 알아보자.
1) input iterator : 전진만 가능, 한번에 하나만 읽기 가능
2) Output iterator : 전진만 가능, 한번에 하나만 쓰기 가능
3) Forward iterator : 전진만 가능, 한번에 여러 개 읽고,쓰기 가능
4) Bidirectional iterator : 전/후진 모두 가능, 한번에 여러 개 읽고, 쓰기 가능
5) Random access iterator : 반복자를 임의의 위치만큼 전/후진 가능 (가장 강력하며, 반복자 산술 연산을 통해 상수시간에 계산가능한 반복자)
( reference : https://www.geeksforgeeks.org/introduction-iterators-c/)
이제 advance 템플릿은 위 반복자를 다음과 같이 컨트롤한다.
template<typename IterT, typename DistT>
void advance(IterT& iter, DistT d); // iter를 d단위만큼 전진시킨다, d<0이면 iter를 후진시킨다.
어쨌든, 이러한 다섯 개의 반복자를 식별하기 위해 다음과 같은 "태그(tag) 구조체"가 C++ 표준 라이브러리에 정의되어 있다.
struct input_iterator_tag{};
struct output_iterator_tag{};
struct forward_iterator_tag : public input_iterator_tag{};
struct bidirectional_iterator_tag : public forward_iterator_tag{};
struct random_access_iterator_tag : public bidirectional_iterator_tag{};
동일한 관계이다. (is -a 관계)
advance가 정말로 하고싶은 것은 iterator를 종류별로 구분하는것이다. ( 임의 접근 반복자를 사용하면, 상수시간에 산술이 가능하므로)
이를 위해 특성정보(traits)를 이용한다.
2. 특성정보 클래스
특성정보란, 컴파일 도중에 어떤 주어진 타입의 정보를 얻을 수 있게 하는 객체를 뜻한다.
특성정보는 c++에서 미리정의된 문법구조가 아니며, 키워드도 아니다. 그냥 c++프로그래머들이 따르는 구현 기법이며, 관례이다.
특성정보는 구조체로 구현하나, 특성정보 클래스라 부른다.
특성정보는 기본제공타입에 대해 쓸 수 있어야 한다.
즉, 위에서 언급한 advance 함수는 포인터(IterT)와 int(distT)를 받아서 호출될때도 정상작동해야한다.
이를 위해, 표준적은 특성정보를 담은 기본템플릿과, 특수화 버전(특정기능을 위해, ex. random_access_iterator)의 템플릿이 요구된다.
3. Iterator_traits 클래스
1) 사용자정의 반복자 타입에 대한 구현을 요구하며, 사용자 정의타입으로 하여금 iterator_category라는 이름의 타입을 가질 것을 요구한다. (아래 deque는 queue의 개념상 랜덤 액세스 방식을 요구한다.)
template < ... >
class deque {
public :
class iterator {
public:
typedef random_access_iterator_tag iterator_category; // 요 부분!
...
};
...
};
2) 위 예제에서 사용자 정의한 타입인 ( ranrandom_access_iterator_tag iterator_category ) 을 iterator_traits 구조체에서 정의한다.
template<class _Iter>
struct iterator_traits
{
// get traits from iterator _Iter
typedef typename _Iter::iterator_category iterator_category;
};
2-1) 위와 같이 표준 iterator_traits클래스를 만들었다면, 부분특수화를 통해 기본 제공 타입에 대한 버전도 만들어줘야한다.
template<class _Ty>
struct iterator_traits<_Ty *> // 또는 <const _Ty*>
{
// get traits from pointer
// 포인터는 산술연산이 가능하므로, 바로 random_access_iterator_tag를 지정해준다
typedef random_access_iterator_tag iterator_category;
};
*해설 : 1)예제에서 iterator_category라는 이름을 typedef 하고 있으며 이타입을 iterator_traits로 넘긴다.
그럼 iterator_traits에서 deque::iterator_category 타입을 다시 iterator_category 로 typedef 하였다.
그다음 iterator_traits 자체를 템플릿 인자를 넘긴다면? random_access_iterator_tag 구조체가 생성되는 것이다.
4. 실전 예제
advance함수는 인자로 넘어온 iterator에 맞는 로직처리를 진행할 것이다. (분기처리)
이것을 if else로 실행타임에 분기하는 것이아니라, 오버로딩을 이용하여 컴파일 타임에 해결하는 것이다.
template<class _Iter> inline
typename iterator_traits<_Iter>::iterator_category _Iter_cat(const _Iter&)
{
// return category from iterator argument
typename iterator_traits<_Iter>::iterator_category _Cat;
return (_Cat);
}
template<class _InIt, class _Diff>
inline void advance(_InIt& _Where, _Diff _Off)
{
// increment iterator by offset, arbitrary iterators
_Advance(_Where, _Off, _Iter_cat(_Where));
}
/-
각 iterator_tag로 overload된 함수들
*-
template<class _InIt, class _Diff>
inline void _Advance(_InIt& _Where, _Diff _Off, input_iterator_tag)
{
// ...
}
template<class _FI, class _Diff>
inline void _Advance(_FI& _Where, _Diff _Off, forward_iterator_tag)
{
// ...
}
*풀이 : 앞서 말한것처럼 위예제의 첫번째 함수의 매개변수인 _Iter&를 통해 넘어온 반복자 를 템플릿의 인자로 넣어 iterator_traits 객체를 반환한다. -> 반환된 객체를 오버로딩된 _Advance() 에서 처리한다. 즉, 컴파일 타임에 분기가 가능하다.
이것만은 잊지 말자!
1. 특성정보 클래스는 컴파일 도중에 사용할 수 있는 타입 관련 정보를 만들어낸다. 또한 특성정보 클래스는 템플릿 및 템플릿 특수 버전을 사용하여 구현한다.
2. 함수 오버로딩 기법과 결합하여 특성정보 클래스를 사용하면, 컴파일 타임에 결정되는 타입별 if... else 기능을 구사할 수 있다.
(참고사이트1. : http://egloos.zum.com/sweeper/v/3007176)
특성정보(traits) 클래스
0. 들어가기에 앞서... 이 글의 카테고리를 프로그래밍 일반으로 할 지 STL로 할 지 꽤나 많은 고민을 했다. 특성정보 클래스는 STL에서 많이 쓰이지만, 비단 STL에 국한되지 않는 내용이기 때문이다. 템플릿은 컴파일 타임에 인스턴스화 되므로, 다양한 템플릿 클래스에 대해 처리하는 함수를 만들 때 컴파일 타임에 이 녀석이 어떤 녀석인지 알아
egloos.zum.com
'책 정리 > Effective C++ 3rd' 카테고리의 다른 글
항목 49. new 처리자의 동작원리를 제대로 이해하자 (0) | 2020.04.01 |
---|---|
항목 48. 템플릿 메타프로그래밍, 하지 않겠는가? (0) | 2020.03.27 |
항목 46. 타입 변환이 바람직할 경우에는 비멤버 함수를 클래스 템플릿 안에 정의해 두자. (0) | 2020.03.25 |
항목 45. "호환되는 모든 타입"을 받아들이는 데는 멤버 함수 템플릿이 직방! (0) | 2020.03.24 |
항목 44. 매개변수에 독립적인 코드는 템플릿으로부터 분리시키자 (0) | 2020.03.24 |
댓글