본문 바로가기
책 정리/Effective C++ 3rd

항목 47. 타입에 대한 정보가 필요하다면 특성정보 클래스를 사용하자

by ocean20 2020. 3. 26.

템플릿 컴파일 타임에 인스턴스화 된다. 따라서 다양한 템플릿클래스를 처리하는 함수를 만들때 우리는 특성정보 클래스를 이용하여 컴파일 타임에 해당 클래스가 어떤 타입인지 밝혀낸다.

 

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

 

댓글