cpp-ranges

C++20 ranges는 구간을 가벼운 view로 다루어 원소를 복사하지 않고 필터(filter)·변환(transform)·**정렬(sort)**을 합성하게 하는 표준 라이브러리입니다.

이 글은 cppreference의 ranges·constrained algorithms·filter_view·transform_view 문서를 2026-09-01 기준으로 정리한 일반 설명이며, 표준 버전과 구현에 따라 세부 동작은 달라질 수 있습니다.

ranges는 무엇인가?

한 줄 답: 이 라이브러리는 알고리즘과 이터레이터 라이브러리를 확장·일반화하여 가벼운 간접 시퀀스를 조합 가능한 파이프라인으로 다루게 합니다.

<ranges> 헤더로 제공되는 이 C++20 라이브러리는 알고리즘과 이터레이터 라이브러리를 더 조합하기 쉽고 오류가 적은 형태로 확장합니다. 여기서 view는 반복 가능한 시퀀스를 직접 저장하기보다 간접적으로 나타내는 가벼운 객체입니다.

가장 기본적인 범위 추상화는 [begin, end) 이터레이터 쌍입니다. 컨테이너는 이 구간으로 암시적으로 다뤄질 수 있고, 이터레이터 쌍을 받던 알고리즘에는 하나의 범위 인자를 받는 오버로드가 제공됩니다. 예를 들어 std::ranges::sort에는 컨테이너를 직접 넘기는 형태가 있습니다.

이 라이브러리의 range adaptor는 view에 **lazy(지연)**하게 적용되고, range algorithm은 **eager(즉각)**하게 적용됩니다. 어댑터를 |로 연결하면 파이프라인이 만들어지며, 실제 동작은 view를 순회하는 동안 이어집니다. counted sequence, 조건으로 끝나는 sequence, 끝이 정해지지 않은 sequence도 이 추상화로 표현할 수 있지만, 여기서는 존재만 확인합니다.

std::viewsstd::ranges::views의 별칭입니다. 따라서 std::views::filter처럼 짧게 적어도 같은 view adaptor 네임스페이스를 가리킵니다.

view와 컨테이너는 무엇이 다른가?

한 줄 답: 컨테이너가 원소와 저장 공간을 관리하는 객체라면, 전형적인 어댑터가 만드는 view는 기반 구간을 나타내며 원소를 복사하지 않는 가벼운 범위입니다.

Views do not own. 전형적인 filter_viewtransform_view는 컨테이너의 원소를 새로 복사하지 않고 기반 시퀀스를 참조하는 방식으로 구간을 표현합니다.

view 개념은 range adaptor 파이프라인을 구성하기에 적합한 의미적 속성을 범위 타입에 요구합니다. 이동 생성은 상수 시간이어야 하고, 복사 가능한 타입이라면 복사 생성도 상수 시간이어야 합니다. 대입과 소멸 역시 저비용이어야 합니다.

원소를 보관하는 복사 가능한 컨테이너인 std::vector는 일반적으로 이 의미적 요구를 만족하지 않습니다. 컨테이너를 복사할 때 모든 원소를 함께 복사하므로 상수 시간에 처리할 수 없기 때문입니다. 다만 view가 반드시 비소유 타입이어야 하는 것은 아니며 owning_view도 존재합니다.

연속 저장소를 가리키는 비소유 뷰는 span 글의 주제이며, 여기서는 어댑터 파이프라인에 쓰이는 가벼운 구간만 다룹니다.

filter와 transform은 어떻게 쓰나?

한 줄 답: std::views::filter로 술어를 만족하지 않는 원소를 제외하고 std::views::transform으로 남은 원소에 함수를 적용하면, 순회 시점에 결과를 얻는 파이프라인을 만들 수 있습니다.

적합한 식 e와 술어 p에 대해 std::views::filter(e, p)std::ranges::filter_view(e, p)와 expression-equivalent입니다. 또한 std::views::filter(pred)처럼 closure를 만들어 파이프라인에 넣을 수 있습니다. 변환도 같은 방식으로 std::views::transform(e, f)std::ranges::transform_view(e, f)가 대응하고, std::views::transform(fun)을 closure로 사용할 수 있습니다.

#include <iostream>
#include <ranges>

int main()
{
    auto const ints = {0, 1, 2, 3, 4, 5};
    auto even = [](int i) { return 0 == i % 2; };
    auto square = [](int i) { return i * i; };

    for (int i : ints | std::views::filter(even) | std::views::transform(square))
        std::cout << i << ' ';
}

위 코드는 공식 문서의 짝수 필터와 제곱 예시이며, 문서에 제시된 출력은 0 4 16입니다.

어댑터는 파이프라인을 만드는 순간에 술어와 변환 함수를 실행하지 않고, view를 반복할 때 lazy하게 적용합니다. 같은 조합은 std::views::transform(std::views::filter(ints, even), square)처럼 함수형 표기로도 나타낼 수 있습니다.

C++ 기술면접 질문 10가지는 이 파이프라인과는 다른 범위의 C++ 핵심 개념을 점검하는 참고 글입니다.

알고리즘은 왜 ranges 쪽을 쓰나?

한 줄 답: 이 알고리즘은 이터레이터·센티널 쌍이나 하나의 범위 인자를 받고, 제약과 projection을 확인한 뒤 대상 구간을 즉시 변경합니다.

C++20의 constrained algorithm 대부분은 std::ranges 네임스페이스에 있습니다. 이 알고리즘은 이터레이터·센티널 쌍을 받는 형태와 하나의 range 인자를 받는 형태를 제공하므로, 컨테이너의 시작과 끝을 호출부에서 따로 적지 않아도 됩니다.

std::ranges::sort의 범위 오버로드는 std::ranges::random_access_rangestd::sortable 요구를 확인합니다. 비교자의 기본값은 std::ranges::less이고 projection의 기본값은 std::identity입니다.

#include <algorithm>
#include <array>

std::array s{5, 7, 4, 2, 8, 6, 1, 9, 0, 3};
std::ranges::sort(s);

이 호출은 s.begin(), s.end() 쌍이 아니라 컨테이너 하나를 범위 인자로 전달합니다. 정렬은 eager하게 즉시 수행되어 대상 구간을 변경하며, 동등한 원소의 상대 순서는 보장되지 않습니다. 반환값은 대상 범위의 past-the-end 이터레이터이고, 복잡도는 compproj를 O(N log N)번 적용하는 수준으로 설명됩니다.

projection에 멤버 포인터를 전달하면 별도의 비교자를 작성하지 않고 비교할 필드를 선택할 수 있습니다. 이는 lazy하게 순회하는 std::views::filterstd::views::transform이 정렬을 수행하거나 스스로 새 컨테이너에 복사하는 것과 구별되는 지점입니다.

FAQ

한 줄 답: 원소를 복사하지 않는 view 조합과 즉시 대상 구간을 바꾸는 정렬 알고리즘을 실행 시점과 요구 조건으로 구분하면 됩니다.

C++에서 구간을 복사 없이 다루려면 무엇을 사용합니까? <ranges> 헤더의 view 어댑터를 사용하면 기반 시퀀스를 직접 복사하지 않고 필터와 변환을 조합할 수 있습니다.

view는 컨테이너의 원소를 소유합니까? Views do not own. 전형적인 filter_viewtransform_view는 기반 시퀀스를 나타내며 원소를 복사하지 않습니다. 다만 비소유가 view 개념의 필수 조건은 아니고 owning_view도 있습니다.

filter와 transform은 언제 실행됩니까? 어댑터는 lazy하므로 파이프라인을 만들 때가 아니라 view를 순회할 때 술어와 변환 함수를 적용합니다.

std::sort 대신 std::ranges::sort를 사용하는 이유는 무엇입니까? 범위 오버로드에 컨테이너를 하나의 인자로 넘길 수 있고, random_access_rangesortable 제약을 확인하며, projection으로 비교 필드를 선택할 수 있습니다. 호출은 대상 구간을 즉시 정렬합니다.

출처

한 줄 답: 본문은 2026-09-01 기준으로 확인한 cppreference의 C++20 문서만 근거로 구성합니다.

  • C++20 라이브러리 개요 — view, lazy adaptor와 eager algorithm, 파이프라인, std::views 별칭을 확인했습니다.
  • view 개념 — 파이프라인에 적합한 의미적 요구, 복사·이동 성질, 컨테이너와 소유권의 차이를 확인했습니다.
  • filter_view — 조건에 맞지 않는 원소를 제외하는 어댑터와 closure 형태를 확인했습니다.
  • transform_view — 각 원소에 변환 함수를 적용하는 어댑터와 closure 형태를 확인했습니다.
  • constrained algorithms — 이터레이터·센티널 쌍, 단일 범위 인자, projection을 확인했습니다.
  • std::ranges::sort — 범위 오버로드의 제약, 기본 비교자와 projection, 반환값, 정렬 동작과 복잡도를 확인했습니다.