숏컷 코드
std::vector<std::string> names{"Mina", "Jin", "Soo"};
for (const auto& name : names) { // 읽기 전용 (권장)
std::cout << name << "\n";
}
for (auto& name : names) { // 원소 수정
name += "!";
}
for (auto [key, val] : map) { // 구조적 바인딩 (C++17)
std::cout << key << "=" << val;
}문법
내부 전개 — begin()과 end() 반복자
range-based for는 컴파일러가 begin()/end()를 호출하는 일반 for문으로 변환합니다. begin()과 end()를 제공하는 타입이면 어디든 range-based for를 쓸 수 있습니다.
for (const auto& x : container) { use(x); }
// 컴파일러가 아래로 변환:
{
auto __begin = container.begin();
auto __end = container.end();
for (; __begin != __end; ++__begin) {
const auto& x = *__begin;
use(x);
}
}이 구조 때문에 루프 도중 컨테이너에 원소를 추가하거나 삭제하면 end() 값이 바뀌어 정의되지 않은 동작이 됩니다.
auto 선택 — 복사 vs 참조 vs const 참조
세 가지 선택지는 성능과 의도에서 차이가 납니다.
std::vector<std::string> names{"Mina", "Jin"};
for (auto name : names) { } // string 복사 — 매 반복마다 복사 비용
for (auto& name : names) { } // 참조 — 원본 수정 가능
for (const auto& name : names) { } // const 참조 — 복사 없음, 수정 불가 (권장)int, char 같은 기본 타입은 복사 비용이 없어 auto가 괜찮지만, 클래스 타입은 const auto&가 기본입니다.
C++20 ranges — 뷰 파이프라인
std::ranges는 범위 기반 for와 함께 쓸 수 있는 지연 평가 뷰를 제공합니다.
#include <ranges>
#include <vector>
std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 파이프라인 — 짝수만 필터 후 제곱, 앞 3개
for (int x : v
| std::views::filter([](int n){ return n % 2 == 0; })
| std::views::transform([](int n){ return n * n; })
| std::views::take(3)) {
std::cout << x << " "; // 4 16 36
}
// 다른 뷰들
for (auto& [i, v] : std::views::enumerate(v)) { /* C++23 */ }
for (int x : std::views::iota(1, 6)) // 1 2 3 4 5 (직접 컨테이너 없이)
std::cout << x << " ";
for (int x : std::views::reverse(v)) // 역방향
std::cout << x << " ";사용자 정의 타입에서 사용하기
begin()과 end()를 반환하는 반복자를 제공하면 사용자 정의 타입도 range-based for를 지원합니다.
struct Range {
int lo, hi;
struct Iter {
int val;
int& operator*() { return val; }
Iter& operator++() { ++val; return *this; }
bool operator!=(const Iter& o) const { return val != o.val; }
};
Iter begin() { return {lo}; }
Iter end() { return {hi}; }
};
for (int n : Range{1, 5}) {
std::cout << n << " "; // 1 2 3 4
}체크포인트
| 상황 | 적합한 선택 |
|---|---|
| 읽기만 할 때 | for (const auto& x : c) |
| 원소 수정 | for (auto& x : c) |
| 인덱스가 필요 | 전통 for (int i = 0; ...) |
| map의 키·값 동시 접근 | for (auto& [k, v] : map) (C++17) |
| 역방향 순회 | std::views::reverse(c) (C++20) 또는 역방향 반복자 |
| 조건 필터 + 변환 | c | views::filter(...) | views::transform(...) |
| 숫자 범위 순회 | std::views::iota(start, end) (컨테이너 불필요) |
주의할 점
range-based for 도중 컨테이너를 수정(push_back, erase)하면 반복자가 무효화되어 정의되지 않은 동작이 됩니다.
std::vector<int> v{1, 2, 3, 4, 5};
// ❌ 순회 중 push_back — vector 재할당으로 반복자 무효화
for (const auto& x : v) {
if (x == 3) v.push_back(6); // UB
}
// ✅ 수정할 원소를 먼저 수집, 루프 후 적용
std::vector<int> to_add;
for (const auto& x : v) {
if (x == 3) to_add.push_back(6);
}
v.insert(v.end(), to_add.begin(), to_add.end());참고 링크
1 sources