본문 바로가기
개발/C++ (98,03,11,14,17,20,23)

Modern C++ : Custom Iterator Utilities (98, 11, 17, 20)

by snowoods 2025. 8. 29.

Modern C++

Custom Iterator Utilities in C++

 

개요

이 문서는 C++ 표준 라이브러리의 반복자 유틸리티 함수들을 직접 구현한 예제를 다룹니다. advance, distance, next, prev 함수들을 구현하여 반복자의 동작 방식을 이해하고, 이를 활용하는 방법을 설명합니다.

 

C++ 버전별 주요 키워드 도입 시기

  • C++98: 기본 반복자 유틸리티 도입
  • C++11: std::next(), std::prev() 함수 추가
  • C++17: std::size()와 같은 반복자 관련 유틸리티 추가
  • C++20: std::ranges와 함께 개선된 반복자 지원

 

내용 설명

구현된 타입과 함수들

DifferenceType

  • 반복자 간의 거리를 표현하는 정수 타입입니다.
  • 실제 정의: using DifferenceType = It::difference_type;
    • Itstd::vector<std::int32_t>::iterator의 별칭입니다.
    • std::vector의 반복자는 random_access_iterator이므로, difference_typestd::ptrdiff_t와 동일한 타입입니다.
      • std::ptrdiff_t는 두 포인터 간의 차이를 저장할 수 있는 부호 있는 정수 타입입니다.
      • <cstddef> 헤더에 정의되어 있으며, 포인터 연산의 결과를 저장하기에 충분한 크기를 가집니다.
      • 32비트 시스템에서는 일반적으로 4바이트, 64비트 시스템에서는 8바이트 크기입니다.
      • sizeof(std::ptrdiff_t) == sizeof(void*)가 일반적입니다.
  • std::iterator_traits<It>::difference_type에서도 동일한 타입을 얻을 수 있습니다.
  • 반복자 간의 거리나 이동 거리를 표현할 때 사용됩니다.
  • 부호 있는 정수 타입으로, 양수와 음수 모두 표현 가능합니다.
  • 실제 크기는 플랫폼에 따라 다르지만, 일반적으로 64비트 시스템에서는 8바이트(64비트) 크기를 가집니다.

 

주요 함수들

  1. advance
    • 반복자를 n만큼 전진 또는 후진시킵니다.
    • 양수 n: 앞으로 n칸 이동
    • 음수 n: 뒤로 n칸 이동
  2. distance
    • 두 반복자 사이의 거리를 반환합니다.
    • 첫 번째 반복자부터 두 번째 반복자까지의 요소 수를 계산합니다.
  3. next
    • 현재 반복자에서 n칸 앞에 있는 반복자를 반환합니다.
    • 기본값으로 1칸 앞의 반복자를 반환합니다.
  4. prev
    • 현재 반복자에서 n칸 뒤에 있는 반복자를 반환합니다.
    • 기본값으로 1칸 뒤의 반복자를 반환합니다.

 

예제 코드

#include <cstdint>
#include <iterator>
#include <vector>

namespace mystd
{
    using It = std::vector<std::int32_t>::iterator;
    using DifferenceType = It::difference_type;

    void advance(It &it, DifferenceType n)
    {
        while (n > 0)
        {
            ++it;
            --n;
        }

        while (n < 0)
        {
            --it;
            ++n;
        }
    }

    DifferenceType distance(It first, It last)
    {
        auto result = DifferenceType{0};

        while (first != last)
        {
            ++first;
            ++result;
        }

        return result;
    }

    It next(It it, DifferenceType n = 1)
    {
        mystd::advance(it, n);
        return it;
    }

    It prev(It it, DifferenceType n = 1)
    {
        mystd::advance(it, -n);
        return it;
    }
} // namespace mystd

 

실행 예제

#include <iostream>
#include <vector>

int main()
{
    std::vector<int> vector{1, 2, 3};
    auto vector_it = vector.begin();

    // next 사용 예제
    auto next_it = mystd::next(vector_it);
    std::cout << "*next_it: " << *next_it << '\n';  // 출력: 2

    // prev 사용 예제
    auto prev_it = mystd::prev(next_it);
    std::cout << "*prev_it: " << *prev_it << '\n';  // 출력: 1

    // advance 사용 예제
    mystd::advance(vector_it, 2);
    std::cout << "advance: " << *vector_it << '\n';  // 출력: 3

    // distance 사용 예제
    auto dist = mystd::distance(vector.begin(), vector_it);
    std::cout << "distance: " << dist << '\n';  // 출력: 2

    return 0;
}

 

실행 결과

*next_it: 2
*prev_it: 1
advance: 3
distance: 2

 

활용팁

  1. 성능 고려사항
    • distance 함수는 선형 시간(O(n))이 소요될 수 있으므로, 임의 접근 반복자에서는 사용을 지양하고 - 연산자를 사용하는 것이 좋습니다.
  2. 안전성
    • advance 함수는 컨테이너의 범위를 벗어나는지 검사하지 않으므로 주의가 필요합니다.
    • 범위 기반 for 문이나 표준 알고리즘을 사용할 수 있다면, 직접 반복자를 다루는 것보다 이를 우선적으로 고려하세요.
  3. 템플릿 활용
    • 이 예제는 std::vector<int>에 특화되어 있지만, 템플릿을 사용하면 다양한 컨테이너에 적용 가능한 일반적인 구현이 가능합니다.
  4. 디버깅
    • 복잡한 반복자 연산 시 중간 결과를 확인하기 위해 std::distance와 같은 함수를 활용하여 디버깅할 수 있습니다.