<ranges>
개념
개념은 컴파일 시간에 템플릿 매개 변수를 제한하는 C++20 언어 기능입니다. 잘못된 템플릿 인스턴스화를 방지하고, 읽을 수 있는 형식으로 템플릿 인수 요구 사항을 지정하고, 더 간결한 템플릿 관련 컴파일러 오류를 제공하는 데 도움이 됩니다.
다음 예제에서는 나누기를 지원하지 않는 형식으로 템플릿을 인스턴스화하지 않도록 하는 개념을 정의합니다.
// requires /std:c++20 or later
#include <iostream>
// Definition of dividable concept which requires
// that arguments a & b of type T support division
template <typename T>
concept dividable = requires (T a, T b)
{
a / b;
};
// Apply the concept to a template.
// The template will only be instantiated if argument T supports division.
// This prevents the template from being instantiated with types that don't support division.
// This could have been applied to the parameter of a template function, but because
// most of the concepts in the <ranges> library are applied to classes, this form is demonstrated.
template <class T> requires dividable<T>
class DivideEmUp
{
public:
T Divide(T x, T y)
{
return x / y;
}
};
int main()
{
DivideEmUp<int> dividerOfInts;
std::cout << dividerOfInts.Divide(6, 3); // outputs 2
// The following line will not compile because the template can't be instantiated
// with char* because char* can be divided
DivideEmUp<char*> dividerOfCharPtrs; // compiler error: cannot deduce template arguments
}
컴파일러 스위치 /diagnostics:caret
를 Visual Studio 2022 버전 17.4 미리 보기 4 이상으로 전달하면 개념 dividable<char*>
이 false로 평가되는 오류는 실패한 식 요구 사항을 (a / b)
직접 가리킵니다.
범위 개념은 네임스페이 std::ranges
스에 정의되고 헤더 파일에 선언됩니다 <ranges>
. 범위 어댑터, 뷰 등의 선언에 사용됩니다.
범위는 6가지 범주입니다. 개념에 나열된 <iterator>
반복기의 범주와 관련이 있습니다. 기능 증가 순서에 따라 범주는 다음과 같습니다.
범위 개념 | 설명 |
---|---|
output_range input_range |
쓸 수 있는 범위를 지정합니다. 한 번 읽을 수 있는 범위를 지정합니다. |
forward_range |
여러 번 읽고 쓸 수 있는 범위를 지정합니다. |
bidirectional_range |
앞뒤로 읽고 쓸 수 있는 범위를 지정합니다. |
random_access_range |
인덱스로 읽고 쓸 수 있는 범위를 지정합니다. |
contiguous_range |
요소가 메모리에서 순차적이고 크기가 같으며 포인터 산술 연산을 사용하여 액세스할 수 있는 범위를 지정합니다. |
앞의 표에서는 기능 향상 순서대로 개념이 나열됩니다. 개념의 요구 사항을 충족하는 범위는 일반적으로 앞의 행에 있는 개념의 요구 사항을 충족합니다. 예를 들어, a random_access_range
는 , forward_range
, input_range
및 output_range
.bidirectional_range
예외는 input_range
쓸 수 없으므로 output_range
.
기타 범위 개념은 다음과 같습니다.
범위 개념 | 설명 |
---|---|
range C++20 |
반복기와 sentinel을 제공하는 형식을 지정합니다. |
borrowed_range C++20 |
범위 반복기의 수명이 범위의 수명에 연결되지 않도록 지정합니다. |
common_range C++20 |
범위 반복기의 형식과 범위의 sentinel 형식이 같게 지정합니다. |
Simple_View C++20 |
표준 라이브러리의 일부로 정의된 공식 개념이 아니라 일부 인터페이스에서 도우미 개념으로 사용됩니다. |
sized_range C++20 |
요소 수를 효율적으로 제공할 수 있는 범위를 지정합니다. |
view C++20 |
효율적인(일정한 시간) 이동 생성, 할당 및 소멸이 있는 형식을 지정합니다. |
viewable_range C++20 |
뷰이거나 뷰로 변환할 수 있는 형식을 지정합니다. |
bidirectional_range
A bidirectional_range
는 범위를 앞뒤로 읽고 쓸 수 있습니다.
template<class T>
concept bidirectional_range =
forward_range<T> && bidirectional_iterator<iterator_t<T>>;
매개 변수
T
테스트할 형식이 .인지 확인합니다 bidirectional_range
.
설명
이러한 종류의 범위가 지원되거나 그 이상입니다 bidirectional_iterator
.
A bidirectional_iterator
에는 A의 forward_iterator
기능이 있지만 뒤로 반복할 수도 있습니다.
의 몇 가지 예는 bidirectional_range
std::set
, std::vector
및 std::list
.
borrowed_range
형식은 개체에서 가져온 반복기의 유효성이 개체의 수명보다 오래 지속될 수 있는지를 모델 borrowed_range
화합니다. 즉, 범위가 더 이상 존재하지 않는 경우에도 범위에 대한 반복기를 사용할 수 있습니다.
template<class T>
concept borrowed_range =
range<T> &&
(is_lvalue_reference_v<T> || enable_borrowed_range<remove_cvref_t<T>>);
매개 변수
T
테스트할 형식이 .인지 확인합니다 borrowed_range
.
설명
rvalue 범위의 수명은 범위 모델 borrowed_range
여부에 관계없이 함수 호출 후에 끝날 수 있습니다. 이 경우 범위의 borrowed_range
수명이 종료되는 시기에 관계없이 잘 정의된 동작으로 반복기를 계속 사용할 수 있습니다.
예를 들어 컨테이너와 같은 vector
경우 또는 list
컨테이너의 수명이 종료될 때 반복기가 제거된 요소를 참조하기 때문에 이러한 경우가 사실이 아닙니다.
예를 들어 view
iota_view<int>{0, 42}
반복기가 borrowed_range
요청 시 생성되므로 삭제되지 않는 값 집합을 초과하여 반복기를 계속 사용할 수 있습니다.
common_range
반복기의 common_range
형식은 sentinel의 형식과 동일합니다. 즉, begin()
end()
동일한 형식을 반환합니다.
template<class T>
concept common_range =
ranges::range<T> && std::same_as<ranges::iterator_t<T>, ranges::sentinel_t<T>>;
매개 변수
T
테스트할 형식이 .인지 확인합니다 common_range
.
설명
형식 std::ranges::begin()
std::ranges::end()
을 가져오는 것은 두 반복기 사이의 거리를 계산하는 알고리즘과 반복기 쌍으로 표시된 범위를 허용하는 알고리즘에 중요합니다.
표준 컨테이너(예: vector
)는 의 요구 사항을 충족합니다 common_range
.
contiguous_range
요소의 contiguous_range
메모리에 순차적으로 저장 되 고 포인터 산술을 사용 하 여 액세스할 수 있습니다. 예를 들어 배열은 .입니다 contiguous_range
.
template<class T>
concept contiguous_range =
random_access_range<T> && contiguous_iterator<iterator_t<T>> &&
requires(T& t) {{ ranges::data(t) } -> same_as<add_pointer_t<range_reference_t<T>>>;};
매개 변수
T
테스트할 형식이 .인지 확인합니다 contiguous_range
.
설명
요소가 메모리에 순차적으로 배치되고 크기가 같기 때문에 포인터 산술 연산을 통해 A contiguous_range
에 액세스할 수 있습니다. 이러한 종류의 범위는 continguous_iterator
모든 반복기 중에서 가장 유연한 범위를 지원합니다.
의 몇 가지 예는 contiguous_range
std::array
, std::vector
및 std::string
.
예: contiguous_range
다음 예제에서는 포인터 산술 연산을 사용하여 다음 항목 contiguous_range
에 액세스하는 방법을 보여줍니다.
// requires /std:c++20 or later
#include <ranges>
#include <iostream>
#include <vector>
int main()
{
// Show that vector is a contiguous_range
std::vector<int> v = {0,1,2,3,4,5};
std::cout << std::boolalpha << std::ranges::contiguous_range<decltype(v)> << '\n'; // outputs true
// Show that pointer arithmetic can be used to access the elements of a contiguous_range
auto ptr = v.data();
ptr += 2;
std::cout << *ptr << '\n'; // outputs 2
}
true
2
forward_range
A는 forward_range
범위를 여러 번 읽고 쓸 수 있습니다.
template<class T>
concept forward_range = input_range<T> && forward_iterator<iterator_t<T>>;
매개 변수
T
테스트할 형식이 .인지 확인합니다 forward_range
.
설명
이러한 종류의 범위가 지원되거나 그 이상입니다 forward_iterator
. A는 forward_iterator
범위를 여러 번 반복할 수 있습니다.
input_range
한 input_range
번 읽을 수 있는 범위입니다.
template<class T>
concept input_range = range<T> && input_iterator<iterator_t<T>>;
매개 변수
T
테스트 input_range
할 형식입니다.
설명
형식이 다음 요구 사항을 충족하는 경우 input_range
:
- 함수는
ranges::begin()
.를 반환합니다input_iterator
. 정의되지 않은 동작의input_range
결과로 두 번 이상 호출begin()
합니다. - 반복을 역참조할
input_iterator
수 있으며 매번 동일한 값을 생성합니다. 다중input_range
패스가 아닙니다. 반복기를 증분하면 복사본이 무효화됩니다. - 와 함께
ranges::for_each
사용할 수 있습니다. - 더 이상 지원합니다
input_iterator
.
output_range
output_range
은 작성할 수 있는 범위입니다.
template<class R, class T>
concept output_range = range<R> && output_iterator<iterator_t<R>, T>;
매개 변수
R
범위의 형식입니다.
T
범위에 쓸 데이터의 형식입니다.
설명
의미 output_iterator<iterator_t<R>, T>
는 형식이 형식의 범위에 R
형식 T
값을 쓸 수 있는 반복기를 제공한다는 것입니다. 즉, 지원하거나 그 이상입니다 output_iterator
.
random_access_range
A는 random_access_range
인덱스별로 범위를 읽거나 쓸 수 있습니다.
template<class T>
concept random_access_range =
bidirectional_range<T> && random_access_iterator<iterator_t<T>>;
매개 변수
T
테스트할 형식이 .인지 확인합니다 sized_range
.
설명
이러한 종류의 범위가 지원되거나 그 이상입니다 random_access_iterator
. A random_access_range
에는 , output_range
, forward_range
및 bidirectional_range
.input_range
A random_access_range
는 정렬할 수 있습니다.
의 몇 가지 예는 random_access_range
std::vector
, std::array
및 std::deque
.
range
형식이 충족되어야 하는 요구 사항을 정의합니다 range
. A range
는 해당 요소를 반복할 수 있도록 반복기와 센티넬을 제공합니다.
template<class T>
concept range = requires(T& rg)
{
ranges::begin(rg);
ranges::end(rg);
};
매개 변수
T
테스트할 형식이 .인지 확인합니다 range
.
설명
요구 사항은 다음과 같습니다.range
- 다음을 사용하여
std::ranges::begin()
반복할 수 있습니다.std::ranges::end()
ranges::begin()
분할ranges::end()
상환 상수 시간에 실행하고 수정하지range
않습니다. 분할 상환 상수 시간은 O(1)를 의미하지는 않지만, 최악의 경우에도 일련의 호출에 대한 평균 비용은 O(n^2) 또는 더 나쁜 것이 아니라 O(n)입니다.[ranges::begin(), ranges::end())
는 유효한 범위를 표시합니다.
Simple_View
A Simple_View
는 일부 ranges
인터페이스에서 사용되는 박람회 전용 개념입니다. 라이브러리에 정의되어 있지 않습니다. 일부 범위 어댑터의 동작을 설명하는 데 도움이 되도록 사양에만 사용됩니다.
template<class V>
concept Simple_View = // exposition only
ranges::view<V> && ranges::range<const V> &&
std::same_as<std::ranges::iterator_t<V>, std::ranges::iterator_t<const V>> &&
std::same_as<std::ranges::sentinel_t<V>, std::ranges::sentinel_t<const V>>;
매개 변수
V
테스트할 형식이 .인지 확인합니다 Simple_View
.
설명
보기 V
는 Simple_View
다음이 모두 true인 경우입니다.
V
은 뷰입니다.const V
은 범위입니다.const V
둘 다v
동일한 반복기 및 sentinel 형식을 가집니다.
sized_range
A sized_range
는 분할 상환 상수 시간에 범위의 요소 수를 제공합니다.
template<class T>
concept sized_range = range<T> &&
requires(T& t) { ranges::size(t); };
매개 변수
T
테스트할 형식이 .인지 확인합니다 sized_range
.
설명
요구 사항은 sized_range
호출하는 ranges::size
것입니다.
- 범위를 수정하지 않습니다.
- 분할 상환 상수 시간의 요소 수를 반환합니다. 분할 상환 상수 시간은 O(1)를 의미하지는 않지만, 최악의 경우에도 일련의 호출에 대한 평균 비용은 O(n^2) 또는 더 나쁜 것이 아니라 O(n)입니다.
의 몇 가지 예 sized_range
는 다음과 같습니다std::vector
std::list
.
예: sized_range
다음 예제에서는 다음 중 int
하나는 vector
다음과 같습니다.sized_range
// requires /std:c++20 or later
#include <ranges>
#include <iostream>
#include <vector>
int main()
{
std::cout << std::boolalpha << std::ranges::sized_range<std::vector<int>> << '\n'; // outputs "true"
}
view
A view
에는 요소 수에 관계없이 일정한 시간 이동 생성, 할당 및 소멸 작업이 있습니다. 뷰를 복사 생성 가능하거나 할당 가능한 복사할 필요는 없지만, 뷰가 있는 경우 해당 작업도 일정한 시간에 실행해야 합니다.
일정한 시간 요구 사항 때문에 보기를 효율적으로 작성할 수 있습니다. 예를 들어 호출input
된 int
벡터, 숫자를 3으로 나눌 수 있는지 여부를 결정하는 함수, 숫자를 제곱하는 함수를 지정하면 문 auto x = input | std::views::filter(divisible_by_three) | std::views::transform(square);
은 입력에 있는 숫자의 제곱을 3으로 나눌 수 있는 뷰를 효율적으로 생성합니다. 뷰를 함께 |
연결하는 것을 뷰 작성이라고 합니다. 형식이 개념을 충족하는 view
경우 효율적으로 작성할 수 있습니다.
template<class T>
concept view = ranges::range<T> && std::movable<T> && ranges::enable_view<T>;
매개 변수
T
뷰인지 확인하기 위해 테스트할 형식입니다.
설명
보기를 구성 가능하게 만드는 필수 요구 사항은 이동/복사가 저렴하다는 것입니다. 이는 뷰가 다른 보기로 구성될 때 이동/복사되기 때문입니다. 이동 가능한 범위여야 합니다.
ranges::enable_view<T>
는 개념의 의미 체계 요구 사항을 view
준수하는 데 사용되는 특성입니다. 형식은 다음을 통해 옵트인할 수 있습니다.
- 의 특수화에서 공개적으로 그리고 명확하게 파생
ranges::view_interface
- 빈 클래스
ranges::view_base
에서 공개적으로 및 명확하게 파생되거나 - 특
ranges::enable_view<T>
화true
또한 작성해야 하는 상용구 코드를 저장하는 기본 구현을 제공하기 때문에 view_interface
옵션 1이 선호됩니다.
실패하면 옵션 2가 옵션 3보다 약간 간단합니다.
옵션 3의 장점은 형식의 정의를 변경하지 않고도 가능하다는 것입니다.
viewable_range
A viewable_range
는 뷰이거나 뷰로 변환할 수 있는 형식입니다.
template<class T>
concept viewable_range =
range<T> && (borrowed_range<T> || view<remove_cvref_t<T>>);
매개 변수
T
테스트할 형식이 뷰인지 아니면 뷰로 변환할 수 있는지 확인합니다.
설명
범위를 보기로 변환하는 데 사용합니다 std::ranges::views::all()
.