ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 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++에는 여러 가지 특성 정보가 존재하므로 필요에 따라 사용하면 된다.

     

    댓글

Designed by Tistory.