핵심 정리
cpp
std::vector<int> v{1, 2, 3, 4, 5};
// 반복자로 순회
for (auto it = v.begin(); it != v.end(); ++it) {
std::cout << *it << " ";
}
// 알고리즘에 반복자 범위 전달
auto pos = std::find(v.begin(), v.end(), 3);
if (pos != v.end()) std::cout << *pos;
// 역방향 반복자
for (auto it = v.rbegin(); it != v.rend(); ++it) { ... }반복자는 "컨테이너 원소를 가리키는 일반화된 위치"입니다. 중요한 건 문법보다도, 어떤 컨테이너에서 어떤 알고리즘이 가능한지와 수정 후 반복자가 살아 있는지입니다.
문법
먼저 다시 보는 기본형은 아래입니다.
- 순방향 순회:
begin()/end() - 역방향 순회:
rbegin()/rend() - 알고리즘 범위 전달:
[first, last) - 이동 유틸리티:
advance,next,prev - 수정 후 재사용:
erase반환값
어떤 반복자 질문을 먼저 보면 되나
| 질문 | 먼저 확인할 것 |
|---|---|
이 컨테이너가 std::sort 가능한가 | random access iterator인지 |
| 순회 중 삭제가 필요한가 | erase 반환값 사용 여부 |
| 앞으로만 가면 되는가 | input/forward iterator인지 |
| 뒤로도 가야 하는가 | bidirectional iterator인지 |
반복자 — 알고리즘과 컨테이너를 분리하는 추상화
C++ STL 알고리즘(std::find, std::sort 등)은 반복자를 통해 컨테이너에 접근합니다. 덕분에 std::sort는 vector, array, deque 등 어떤 컨테이너든 순서를 보장하는 반복자만 있으면 동작합니다.
cpp
std::vector<int> v{3, 1, 2};
std::sort(v.begin(), v.end()); // ✅ vector
std::array<int, 3> a{3, 1, 2};
std::sort(a.begin(), a.end()); // ✅ array
int arr[] = {3, 1, 2};
std::sort(std::begin(arr), std::end(arr)); // ✅ 배열반복자 카테고리 — 컨테이너마다 다른 능력
반복자는 지원하는 연산에 따라 5가지 카테고리로 분류됩니다. 알고리즘은 필요한 카테고리를 요구합니다.
| 카테고리 | 연산 | 컨테이너 예시 |
|---|---|---|
| 입력(input) | 전진, 읽기 | istream_iterator |
| 출력(output) | 전진, 쓰기 | ostream_iterator |
| 순방향(forward) | 전진, 읽기/쓰기 | forward_list |
| 양방향(bidirectional) | 전진/후진 | list, map, set |
| 임의 접근(random access) | 임의 이동, [] | vector, array, deque |
cpp
std::list<int> lst{1, 2, 3};
// ❌ list 반복자는 임의 접근 불가
// std::sort(lst.begin(), lst.end()); // 컴파일 오류
// ✅ list는 자체 sort 멤버 함수 제공
lst.sort();std::advance / distance / next / prev — 반복자 이동 유틸리티
임의 접근 반복자가 아닌 경우(list, set 등)는 +n으로 직접 이동할 수 없습니다. 유틸리티 함수를 사용합니다.
cpp
#include <iterator>
std::list<int> lst{10, 20, 30, 40, 50};
auto it = lst.begin();
std::advance(it, 3); // it를 3칸 전진 — 제자리 이동 (반환 없음)
std::cout << *it; // 40
auto it2 = lst.begin();
auto it3 = std::next(it2, 2); // it2에서 2칸 앞 — 새 반복자 반환
auto it4 = std::prev(lst.end()); // end()에서 1칸 뒤 — 마지막 원소
auto dist = std::distance(lst.begin(), it); // 반복자 간 거리 = 3cpp
// vector는 +n 직접 가능하지만, 범용 코드는 next/advance 권장
std::vector<int> v{1, 2, 3, 4, 5};
auto mid = std::next(v.begin(), v.size() / 2); // 중간 원소cpp
std::list<int> lst{10, 20, 30};
// ❌ list 반복자는 +n 지원 안 함
// auto it = lst.begin() + 2;
// ✅ 범용 이동 유틸리티 사용
auto it = std::next(lst.begin(), 2);반복자 무효화
컨테이너를 수정하면 기존 반복자가 무효화될 수 있습니다.
cpp
std::vector<int> v{1, 2, 3, 4};
auto it = v.begin() + 2; // v[2]를 가리킴
v.erase(v.begin()); // 앞 원소 삭제 → it 무효화
// *it; // ❌ UB — 무효화된 반복자 역참조
// ✅ erase의 반환값(다음 유효 반복자)을 사용
for (auto it = v.begin(); it != v.end(); ) {
if (*it % 2 == 0)
it = v.erase(it); // 삭제 후 다음 반복자 반환
else
++it;
}체크포인트
- 일반 순방향 순회:
begin()/end() - 역방향 순회:
rbegin()/rend() - 수정 없는 순회:
cbegin()/cend() - 순회 중 삭제:
it = erase(it) - 임의 접근 필요한 알고리즘:
vector/array가 유리 - 임의 접근 안 되는 컨테이너 이동:
advance,next,prev
주의할 점
순회 중 vector에 삽입/삭제하면 반복자가 무효화되어 정의되지 않은 동작이 됩니다.
cpp
std::vector<int> v{1, 2, 3, 4, 5};
// ❌ 순회 중 erase — 반복자 무효화
for (auto it = v.begin(); it != v.end(); ++it) {
if (*it == 3) v.erase(it); // it 무효화 후 ++it → UB
}
// ✅ erase 반환값 사용
for (auto it = v.begin(); it != v.end(); ) {
if (*it == 3) it = v.erase(it);
else ++it;
}
// ✅ erase-remove 관용구 (더 간결)
v.erase(std::remove(v.begin(), v.end(), 3), v.end());cpp
std::list<int> lst{3, 1, 2};
// ❌ list는 random access iterator가 아니어서 std::sort 불가
// std::sort(lst.begin(), lst.end());
// ✅ list는 자체 sort 멤버 함수를 쓴다
lst.sort();참고 링크
1 sources