<ranges>
높은 수준에서 범위 는 반복할 수 있는 범위입니다. 범위는 범위의 시작을 표시하는 반복기와 범위의 끝을 표시하는 센티넬로 표시됩니다. sentinel은 시작 반복기와 동일한 형식이거나 다를 수 있습니다. C++ 표준 라이브러리와 list
같은 vector
컨테이너는 범위입니다. 범위는 STL(표준 템플릿 라이브러리)을 사용하는 기능을 간소화하고 증폭하는 방식으로 반복기를 추상화합니다.
STL 알고리즘은 일반적으로 작동해야 하는 컬렉션 부분을 가리키는 반복기를 사용합니다. 예를 들어 .vector
std::sort()
의 시작과 끝을 표시하는 두 개의 반복기를 전달합니다 vector
. 이는 유연성을 제공하지만 반복기를 알고리즘에 전달하는 것은 모든 것을 정렬하려고 할 수 있기 때문에 추가 작업입니다.
범위를 사용하면 호출 std::ranges::sort(myVector);
할 수 있습니다. 이 호출은 호출 std::sort(myVector.begin(), myVector.end());
한 것처럼 처리됩니다. 범위 라이브러리에서 알고리즘은 범위를 매개 변수로 사용합니다(원하는 경우 반복기를 사용할 수도 있지만). 컬렉션에서 직접 작동할 수 있습니다. 사용할 수 있는 <algorithm>
범위 알고리즘의 예는 다음과 for_each
none_of
find_if_not
find_if
count
find
count_if
any_of
mismatch
copy_n
equal
copy_if
for_each_n
all_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>
함수
다음 함수는 범위에 대한 반복기 및 센티넬을 만들고 범위의 크기를 가져오는 데 사용됩니다.
함수 | 설명 |
---|---|
begin C++20 |
범위의 첫 번째 요소에 대한 반복기를 가져옵니다. |
cbegin C++20 |
범위의 const 첫 번째 요소에 대한 반복기를 가져옵니다. |
cend C++20 |
정규화된 범위의 끝에 있는 sentinel을 const 가져옵니다. |
cdata C++20 |
const 연속 범위의 첫 번째 요소에 대한 포인터를 가져옵니다. |
crbegin C++20 |
범위의 시작 부분에 역방향 const 반복기를 가져옵니다. |
crend C++20 |
반환되는 항목의 crbegin() 끝에 있는 sentinel을 가져옵니다. |
data C++20 |
연속 범위의 첫 번째 요소에 대한 포인터를 가져옵니다. |
empty C++20 |
범위가 비어 있는지 확인합니다. |
end C++20 |
범위의 끝에 있는 sentinel을 가져옵니다. |
rbegin C++20 |
범위의 시작 부분에 역방향 반복기를 가져옵니다. |
rend C++20 |
범위의 끝에 있는 sentinel에 역방향 반복기를 가져옵니다. |
size C++20 |
범위의 크기를 부호 없는 값으로 가져옵니다. |
ssize C++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_t C++20 |
반환된 반복기가 range 수명이 종료된 범위를 참조하는지 확인합니다. |
borrowed_subrange_t C++20 |
반복기가 반환 subrange 되어 수명이 종료된 하위 항목을 참조하는지 확인합니다. |
dangling C++20 |
반환된 반복기가 range /subrange 참조하는 수 range /subrange 명보다 오래 있음을 나타냅니다. |
iterator_t C++20 |
지정된 범위 형식의 반복기 형식을 반환합니다. |
range_difference_t C++20 |
지정된 범위의 반복기 형식의 차이 형식을 반환합니다. |
range_reference_t C++20 |
지정된 범위의 반복기 형식에 대한 참조 형식을 반환합니다. |
range_rvalue_reference_t C++20 |
지정된 범위의 반복기 형식에 대한 rvalue 참조 형식을 반환합니다. 즉, 범위 요소의 rvalue 참조 형식입니다. |
range_size_t C++20 |
지정된 범위의 크기를 보고하는 데 사용되는 형식을 반환합니다. |
range_value_t C++20 |
지정된 범위 반복기 형식의 값 형식을 반환합니다. 즉, 범위에 있는 요소의 형식입니다. |
sentinel_t C++20 |
지정된 범위의 sentinel 형식을 반환합니다. |
이러한 별칭 템플릿에 대한 자세한 내용은 별칭 템플릿을 참조 <ranges>
하세요.