-
Effective C++...45일지 2022. 8. 23. 07:50
타입에 대한 정보가 필요하다면 특성 정보 클래스를 사용하자
STL 반복자의 종류
STL에서 지원하는 반복자는 다음의 다섯 종류가 존재한다.
- 입력 반복자 전진만 가능, 한 칸씩만 이동, 가리키는 위치에서 읽기만 가능, 읽기 가능 횟수 1회 / 읽기 전용 파일 포인터
- 출력 반복자 전진만 가능, 한 칸씩만 이동, 가리키는 위치에서 쓰기만 가능, 쓰기 가능 횟수 1회 / 쓰기 전용 파일 포인터
- 순방향 반복자 입력 반복자, 출력 반복자 처리 가능, 읽기 쓰기 동시에 가능, 횟수 제한 없음 / 해시 컨테이너에서 사용
- 양방향 반복자 순방향 반복자 처리 가능, 뒤로 이동 가능 / list, map, multimap 등에서 사용
- 임의 접근 반복자 양방향 반복자 처리 가능, 임의의 거리만큼 상수 시간에 이동 가능 / vector, deque, string 등에서 사용
이러한 반복자들의 관계는 다음과 같다.
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 { };
특성 정보
컴파일 도중에 주어진 타입의 정보를 얻을 수 있게 해주는 객체를 말하는 것으로 C++에 미리 정의된 문법 구조가 아니고 단순히 구현 기법이다.
특성 정보를 다루는 표준적인 방법은 해당 특성 정보를 템플릿 및 템플릿의 특수화 버전에 넣는 것이다.
// 일반 데이터를 위한 템플릿
template<typename IterT>
struct iterator_traits {
// 타입 정보를 외부에서 접근할 수 있도록 한다.
typedef typename IterT::iterator_category iterator_category;
};
// 포인터 데이터를 위한 템플릿
template<typename IterT>
struct iterator_traits<IterT*> {
// 타입 정보를 외부에서 접근할 수 있도록 한다.
typedef typename IterT::iterator_category iterator_category;
};
template< ... >
class list {
public:
class iterator {
public:
// 클래스에서 지원하는 동작을 typedef로 정의한다.
typedef bidirectional_iterator_tag iterator_category;
};
};
이러한 정보를 이용하는 예제로 이터레이터와 거리가 주어지면 해당 거리만큼 이동하는 advance 함수를 들 수 있다.
template<typename IterT, typename DistT>
void advance(IterT& iter, DistT d)
{
// 상수 계산이 지원되는 경우와 아닌 경우를 나눠서 처리할 수 있을 것이다.
if (typeid(typename std::iterator_traits<IterT>::iterator_category)
== typeid(std::random_access_iterator_tag))
{
iter += d;
}
else
{
if (d >=0) { while (d--) ++iter; }
else { while (d++) --iter; }
}
}
그런데 IterT의 타입은 컴파일 타임에 파악되기 때문에 굳이 실행 시간을 들여 검사를 할 필요가 없다.
함수를 오버 로드하여 상세 작업을 생성하고 상위 함수에서 이 함수에 값을 전달해주기만 하면 된다.
template<typename IterT, typename DistT>
void doAdvance(IterT& iter, DistT d, std::random_access_iterator_tag)
{
iter += d;
}
template<typename IterT, typename DistT>
void doAdvance(IterT& iter, DistT d, std::bidirectional_iterator_tag)
{
if (d >=0) { while (d--) ++iter; }
else { while (d++) --iter; }
}
template<typename IterT, typename DistT>
void doAdvance(IterT& iter, DistT d, std::input_iterator_tag)
{
if (d < 0) {
throw std::out_of_range("Negative distance"); // input_iterator_tag는 전진만 가능하므로 예외를 던진다.
}
while (d--) ++iter;
}
template<typename IterT, typename DistT>
void advance(IterT& iter, DistT d)
{
doAdvance(iter, d, typename std::iterator_triats<IterT>::iterator_category());
}
※ C++에는 여러 가지 특성 정보가 존재하므로 필요에 따라 사용하면 된다.