다음을 통해 공유


<ranges>

높은 수준에서 범위 는 반복할 수 있는 범위입니다. 범위는 범위의 시작을 표시하는 반복기와 범위의 끝을 표시하는 센티넬로 표시됩니다. sentinel은 시작 반복기와 동일한 형식이거나 다를 수 있습니다. C++ 표준 라이브러리와 list같은 vector 컨테이너는 범위입니다. 범위는 STL(표준 템플릿 라이브러리)을 사용하는 기능을 간소화하고 증폭하는 방식으로 반복기를 추상화합니다.

STL 알고리즘은 일반적으로 작동해야 하는 컬렉션 부분을 가리키는 반복기를 사용합니다. 예를 들어 .vector std::sort() 의 시작과 끝을 표시하는 두 개의 반복기를 전달합니다 vector. 이는 유연성을 제공하지만 반복기를 알고리즘에 전달하는 것은 모든 것을 정렬하려고 할 수 있기 때문에 추가 작업입니다.

범위를 사용하면 호출 std::ranges::sort(myVector);할 수 있습니다. 이 호출은 호출 std::sort(myVector.begin(), myVector.end());한 것처럼 처리됩니다. 범위 라이브러리에서 알고리즘은 범위를 매개 변수로 사용합니다(원하는 경우 반복기를 사용할 수도 있지만). 컬렉션에서 직접 작동할 수 있습니다. 사용할 수 있는 <algorithm> 범위 알고리즘의 예는 다음과 for_eachnone_offind_if_notfind_ifcountfindcount_ifany_ofmismatchcopy_nequalcopy_iffor_each_nall_of같습니다.copy

그러나 범위의 가장 중요한 이점은 기능 프로그래밍을 연상시키는 스타일로 범위에서 작동하는 STL 알고리즘을 작성할 수 있다는 것입니다.

범위 예제

범위 이전에 특정 조건을 충족하는 컬렉션의 요소를 변환하려는 경우 작업 간에 결과를 유지하는 중간 단계를 도입해야 했습니다. 예를 들어 세 개로 나눌 수 있는 다른 벡터의 요소에서 사각형의 벡터를 빌드하려는 경우 다음과 같이 작성할 수 있습니다.

std::vector<int> input = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
std::vector<int> intermediate, output;

std::copy_if(input.begin(), input.end(), std::back_inserter(intermediate), [](const int i) { return i%3 == 0; });
std::transform(intermediate.begin(), intermediate.end(), std::back_inserter(output), [](const int i) {return i*i; });

범위를 사용하면 벡터를 사용하지 intermediate 않고도 동일한 작업을 수행할 수 있습니다.

// requires /std:c++20
std::vector<int> input = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

auto output = input
    | std::views::filter([](const int n) {return n % 3 == 0; })
    | std::views::transform([](const int n) {return n * n; });

이 코드는 읽기 쉬울 뿐만 아니라 벡터 및 해당 내용에 intermediate 필요한 메모리 할당을 방지합니다. 또한 두 개의 작업을 구성할 수 있습니다.

앞의 코드에서 3으로 나눌 수 있는 각 요소는 해당 요소를 제곱하는 연산과 결합됩니다. 파이프(|) 기호는 작업을 함께 연결하고 왼쪽에서 오른쪽으로 읽습니다.

결과는 output뷰라고 하는 범위의 일종입니다.

보기는 간단한 범위입니다. 기본 생성, 이동 생성/할당, 복사 생성/할당(있는 경우), 소멸, 시작 및 종료와 같은 보기 작업은 보기의 요소 수에 관계없이 일정한 시간에 발생합니다.

보기는 다음 섹션에서 설명하는 범위 어댑터에 의해 생성됩니다. 다양한 보기를 구현하는 클래스에 대한 자세한 내용은 뷰 클래스를 참조 하세요.

보기의 요소가 표시되는 방식은 보기를 만드는 데 사용하는 범위 어댑터에 따라 달라집니다. 이전 예제에서 범위 어댑터에서 범위를 사용하고 3으로 나눌 수 있는 요소의 뷰를 반환합니다. 기본 범위는 변경되지 않습니다.

보기는 구성 가능하며, 이는 강력합니다. 이전 예제에서 3으로 나눌 수 있는 벡터 요소의 뷰는 해당 요소를 제곱하는 뷰와 결합됩니다.

뷰의 요소는 지연 계산됩니다. 즉, 뷰의 각 요소에 적용하는 변환은 요소를 요청할 때까지 평가되지 않습니다. 예를 들어 디버거에서 다음 코드를 실행하고 줄 auto divisible_by_three = ... auto square = ...에 중단점을 배치하면 각 요소가 input 3씩 분할되도록 테스트할 때 람다 중단점에 도달 divisible_by_three 한 것을 볼 수 있습니다. 람다 중단점은 square 3으로 나눌 수 있는 요소가 제곱되므로 적중됩니다.

// requires /std:c++20
#include <ranges>
#include <vector>
#include <iostream>

int main()
{
    std::vector<int> input =  { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    auto divisible_by_three = [](const int n) {return n % 3 == 0; };
    auto square = [](const int n) {return n * n; };

    auto x = input | std::views::filter(divisible_by_three)
                   | std::views::transform(square);

    for (int i : x)
    {
        std::cout << i << '\n';
    }
    return 0;
}

보기에 대한 자세한 내용은 보기 클래스를 참조 <ranges> 하세요.

범위 어댑터

범위 어댑터는 범위를 가져와 보기를 생성합니다. 범위 어댑터는 지연 평가 뷰를 생성합니다. 즉, 보기를 생성하기 위해 범위의 모든 요소를 변환하는 비용이 발생하지 않습니다. 해당 요소에 액세스할 때 보기에서 요소를 처리하는 데 드는 비용만 지불합니다.

이전 예제 filter 에서 범위 어댑터에서는 3으로 나눌 수 있는 요소가 포함된 뷰 input 를 만듭니다. 범위 어댑터에서는 transform 3으로 나눌 수 있는 요소의 뷰를 사용하고 제곱된 요소의 뷰를 만듭니다.

범위 어댑터는 범위의 힘과 유연성의 핵심인 함께 연결(구성)할 수 있습니다. 범위 어댑터를 작성하면 이전 STL 알고리즘을 쉽게 구성할 수 없는 문제를 해결할 수 있습니다.

뷰를 만드는 방법에 대한 자세한 내용은 범위 어댑터를 참조 하세요.

범위 알고리즘

일부 범위 알고리즘은 범위 인수를 사용합니다. 예제는 std::ranges::sort(myVector);입니다.

범위 알고리즘은 네임스페이스의 해당 반복기 쌍 알고리즘과 std 거의 동일합니다. 차이점은 개념 적용 제약 조건이 있고 범위 인수 또는 더 많은 반복기-sentinel 인수 쌍을 허용한다는 것입니다. 컨테이너에서 직접 작업할 수 있으며 쉽게 연결할 수 있습니다.

<ranges> 함수

다음 함수는 범위에 대한 반복기 및 센티넬을 만들고 범위의 크기를 가져오는 데 사용됩니다.

함수 설명
beginC++20 범위의 첫 번째 요소에 대한 반복기를 가져옵니다.
cbeginC++20 범위의 const 첫 번째 요소에 대한 반복기를 가져옵니다.
cendC++20 정규화된 범위의 끝에 있는 sentinel을 const가져옵니다.
cdataC++20 const 연속 범위의 첫 번째 요소에 대한 포인터를 가져옵니다.
crbeginC++20 범위의 시작 부분에 역방향 const 반복기를 가져옵니다.
crendC++20 반환되는 항목의 crbegin() 끝에 있는 sentinel을 가져옵니다.
dataC++20 연속 범위의 첫 번째 요소에 대한 포인터를 가져옵니다.
emptyC++20 범위가 비어 있는지 확인합니다.
endC++20 범위의 끝에 있는 sentinel을 가져옵니다.
rbeginC++20 범위의 시작 부분에 역방향 반복기를 가져옵니다.
rendC++20 범위의 끝에 있는 sentinel에 역방향 반복기를 가져옵니다.
sizeC++20 범위의 크기를 부호 없는 값으로 가져옵니다.
ssizeC++20 범위의 크기를 서명된 값으로 가져옵니다.

자세한 내용은 함수를 참조 <ranges> 하세요.

범위 개념

범위의 요소를 반복하는 방법은 기본 반복기 형식에 따라 달라집니다. 범위는 지원하는 반복기를 지정하는 C++ 개념을 사용합니다.

C++20에서 개념 X가 개념 Y를 구체화한다고 말하는 것은 개념 Y를 충족하는 모든 것이 개념 X도 만족한다는 것을 의미합니다. 예를 들어 자동차, 버스 및 트럭은 모두 차량을 구체화합니다.

일부 범위 개념은 반복기 범주의 계층 구조를 반영합니다. 다음 표에서는 적용할 수 있는 컨테이너 유형과 함께 범위 개념을 나열합니다.

범위 개념 설명 지원되는 컨테이너
std::ranges::output_range 앞으로 반복할 수 있습니다.
std::ranges::input_range 처음부터 끝까지 한 번 이상 반복할 수 있습니다. std::forward_list
std::unordered_map
std::unordered_multimap
std::unordered_set
std::unordered_multiset
basic_istream_view
std::ranges::forward_range 처음부터 끝까지 두 번 이상 반복할 수 있습니다. std::forward_list
std::unordered_map
std::unordered_multimap
std::unordered_set
std::unordered_multiset
std::ranges::bidirectional_range 앞뒤로 두 번 이상 반복할 수 있습니다. std::list
std::map
std::multimap
std::multiset
std::set
std::ranges::random_access_range 연산자를 사용하여 [] 임의의 요소(상수 시간)에 액세스할 수 있습니다. std::deque
std::ranges::contiguous_range 요소는 메모리에 연속적으로 저장됩니다. std::array
std::string
std::vector

이러한 개념에 대한 자세한 내용은 개념을 참조 <ranges> 하세요.

<ranges> 별칭 템플릿

다음 별칭 템플릿은 범위에 대한 반복기 및 센티넬 유형을 결정합니다.

별칭 템플릿 설명
borrowed_iterator_tC++20 반환된 반복기가 range 수명이 종료된 범위를 참조하는지 확인합니다.
borrowed_subrange_tC++20 반복기가 반환 subrange 되어 수명이 종료된 하위 항목을 참조하는지 확인합니다.
danglingC++20 반환된 반복기가 range/subrange 참조하는 수 range/subrange 명보다 오래 있음을 나타냅니다.
iterator_tC++20 지정된 범위 형식의 반복기 형식을 반환합니다.
range_difference_tC++20 지정된 범위의 반복기 형식의 차이 형식을 반환합니다.
range_reference_tC++20 지정된 범위의 반복기 형식에 대한 참조 형식을 반환합니다.
range_rvalue_reference_tC++20 지정된 범위의 반복기 형식에 대한 rvalue 참조 형식을 반환합니다. 즉, 범위 요소의 rvalue 참조 형식입니다.
range_size_tC++20 지정된 범위의 크기를 보고하는 데 사용되는 형식을 반환합니다.
range_value_tC++20 지정된 범위 반복기 형식의 값 형식을 반환합니다. 즉, 범위에 있는 요소의 형식입니다.
sentinel_tC++20 지정된 범위의 sentinel 형식을 반환합니다.

이러한 별칭 템플릿에 대한 자세한 내용은 별칭 템플릿을 참조 <ranges> 하세요.

참고 항목

<ranges> 함수
<ranges> 개념
범위 어댑터
헤더 파일 참조