반복기 개념
개념은 컴파일 시간에 템플릿 매개 변수를 제한하는 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
스에 정의되며 헤더 파일에 선언됩니다 <iterator>
. 범위 어댑터, 뷰 등의 선언에 사용됩니다.
반복기에는 6가지 범주가 있습니다. 범위 개념에 나열된 범위 범주와 직접 관련이 있습니다.
다음 반복기 개념은 기능 증가 순서대로 나열됩니다. input_or_output_iterator
는 기능 계층 구조의 하위 끝에 있으며 contiguous_iterator
하이 엔드에 있습니다. 계층 구조에서 더 높은 반복기는 일반적으로 낮은 반복기 대신 사용할 수 있지만 그 반대의 경우도 마찬가지입니다. 예를 들어 random_access_iterator
반복기를 대신 forward_iterator
사용할 수 있지만 다른 방법으로는 사용할 수 없습니다. 예외는 input_iterator
쓸 수 없기 때문에 대신 output_iterator
사용할 수 없는 것입니다.
다음 표에서 "다중 패스"는 반복기가 동일한 요소를 두 번 이상 다시 방문할 수 있는지 여부를 나타냅니다. 예를 들어 vector::iterator
반복기의 복사본을 만들고, 컬렉션의 요소를 읽은 다음, 반복기를 복사본의 값으로 복원하고, 동일한 요소를 다시 다시 방문할 수 있기 때문에 다중 전달 반복기가 있습니다. 반복기가 단일 패스인 경우 컬렉션의 요소를 한 번만 방문할 수 있습니다.
다음 표에서 "예제 형식"은 개념을 충족하는 컬렉션/반복기를 나타냅니다.
반복기 개념 | 설명 | Direction | 읽기/쓰기 | 다중 패스 | 예제 형식 |
---|---|---|---|---|---|
input_or_output_iterator C++20 |
반복기 개념 분류의 기초입니다. | 앞으로 | 읽기/쓰기 | 아니요 | istream_iterator , ostream_iterator |
output_iterator C++20 |
쓸 수 있는 반복기를 지정합니다. | 앞으로 | 쓰기 | 아니요 | ostream , inserter |
input_iterator C++20 |
한 번 읽을 수 있는 반복기를 지정합니다. | 앞으로 | 읽기 | 아니요 | istream , istreambuf_iterator |
forward_iterator C++20 |
여러 번 읽고 쓸 수 있는 반복기를 지정합니다. | 앞으로 | 읽기/쓰기 | 예 | vector , list |
bidirectional_iterator C++20 |
앞뒤로 읽고 쓸 수 있는 반복기를 지정합니다. | 앞으로 또는 뒤로 | 읽기/쓰기 | 예 | list , set , multiset , map 및 multimap . |
random_access_iterator C++20 |
인덱스로 읽고 쓸 수 있는 반복기를 지정합니다. | 앞으로 또는 뒤로 | 읽기/쓰기 | 예 | vector , , array deque |
contiguous_iterator C++20 |
요소가 메모리에서 순차적이고 크기가 같으며 포인터 산술 연산을 사용하여 액세스할 수 있는 반복기를 지정합니다. | 앞으로 또는 뒤로 | 읽기/쓰기 | 예 | array , . vector string |
다른 반복기 개념은 다음과 같습니다.
반복기 개념 | 설명 |
---|---|
sentinel_for C++20 |
형식이 반복기 형식의 sentinel임을 지정합니다. |
sized_sentinel_for C++20 |
반복기와 해당 sentinel을 빼서(사용 - ) 일정한 시간에 차이를 찾을 수 있도록 지정합니다. |
bidirectional_iterator
A bidirectional_iterator
는 앞뒤로 읽고 쓰는 것을 지원합니다.
template<class I>
concept bidirectional_iterator =
forward_iterator<I> &&
derived_from<ITER_CONCEPT(I), bidirectional_iterator_tag> &&
requires(I i) {
{--i} -> same_as<I&>;
{i--} -> same_as<I>;
};
매개 변수
I
테스트할 반복기가 .인지 확인 bidirectional_iterator
합니다.
설명
A bidirectional_iterator
에는 A의 forward_iterator
기능이 있지만 뒤로 반복할 수도 있습니다.
와 함께 bidirectional_iterator
사용할 수 있는 컨테이너의 몇 가지 예는 set
, multiset
, map
multimap
, vector
및 list
.
예: bidirectional_iterator
다음 예제에서는 개념을 사용하여 bidirectional_iterator
다음이 있음을 vector<int>
표시합니다.bidirectional_iterator
// requires /std:c++20 or later
#include <iostream>
#include <vector>
int main()
{
std::cout << std::boolalpha << std::bidirectional_iterator<std::vector<int>::iterator> << '\n'; // outputs "true"
// another way to test
std::vector<int> v = {0,1,2};
std::cout << std::boolalpha << std::contiguous_iterator<decltype(v)::iterator>; // outputs true
}
contiguous_iterator
요소가 메모리에서 순차적이고 크기가 같으며 포인터 산술 연산을 사용하여 액세스할 수 있는 반복기를 지정합니다.
template<class I>
concept contiguous_iterator =
random_access_iterator<I> &&
derived_from<ITER_CONCEPT(I), contiguous_iterator_tag> &&
is_lvalue_reference_v<iter_reference_t<I>> &&
same_as<iter_value_t<I>, remove_cvref_t<iter_reference_t<I>>> &&
requires(const I& i) {
{ to_address(i) } -> same_as<add_pointer_t<iter_reference_t<I>>>;
};
매개 변수
I
테스트할 형식이 .인지 확인합니다 contiguous_iterator
.
설명
요소가 메모리에 순차적으로 배치되고 크기가 같기 때문에 포인터 산술 연산을 통해 A contiguous_iterator
에 액세스할 수 있습니다. 의 몇 가지 예는 contiguous_iterator
array
, vector
및 string
.
예: contiguous_iterator
다음 예제에서는 개념을 사용하여 contiguous_iterator
다음 vector<int>
이 있음을 보여줍니다.contiguous_iterator
// requires /std:c++20 or later
#include <iostream>
#include <vector>
int main()
{
// Show that vector<int> has a contiguous_iterator
std::cout << std::boolalpha << std::contiguous_iterator<std::vector<int>::iterator> << '\n'; // outputs "true"
// another way to test
std::vector<int> v = {0,1,2};
std::cout << std::boolalpha << std::contiguous_iterator<decltype(v)::iterator>; // outputs true
}
forward_iterator
의 기능이 input_iterator
있습니다 output_iterator
. 컬렉션을 여러 번 반복할 수 있습니다.
template<class I>
concept forward_iterator =
input_iterator<I> &&
derived_from<ITER_CONCEPT(I), forward_iterator_tag> &&
incrementable<I> &&
sentinel_for<I, I>;
매개 변수
I
테스트할 반복기가 .인지 확인 forward_iterator
합니다.
설명
A는 forward_iterator
앞으로만 이동할 수 있습니다.
와 함께 forward_iterator
사용할 수 있는 컨테이너의 몇 가지 예는 vector
, list
, unordered_set
unordered_multiset
, unordered_map
및 unordered_multimap
.
예: forward_iterator
다음 예제에서는 개념을 사용하여 forward_iterator
다음 vector<int>
이 있음을 보여줍니다.forward_iterator
// requires /std:c++20 or later
#include <iostream>
#include <vector>
int main()
{
// Show that vector has a forward_iterator
std::cout << std::boolalpha << std::forward_iterator<std::vector<int>::iterator> << '\n'; // outputs "true"
// another way to test
std::vector<int> v = {0,1,2};
std::cout << std::boolalpha << std::forward_iterator<decltype(v)::iterator>; // outputs true
}
input_iterator
한 input_iterator
번 이상 읽을 수 있는 반복기입니다.
template<class I>
concept input_iterator =
input_or_output_iterator<I> &&
indirectly_readable<I> &&
requires { typename ITER_CONCEPT(I); } &&
derived_from<ITER_CONCEPT(I), input_iterator_tag>;
매개 변수
I
테스트 input_iterator
할 형식입니다.
설명
input_iterator
두 번 이상 호출 begin()
하면 정의되지 않은 동작이 발생합니다. 모델 input_iterator
만 다중 패스가 아닌 형식입니다. 예를 들어 표준 입력(cin
)에서 읽는 것이 좋습니다. 이 경우 현재 요소를 한 번만 읽을 수 있으며 이미 읽은 문자를 다시 읽을 수 없습니다. 뒤로 input_iterator
만 읽지 않고 앞으로 읽습니다.
예: input_iterator
다음 예제에서는 개념을 사용하여 input_iterator
다음이 있음을 istream_iterator
보여줍니다.input_iterator
// requires /std:c++20 or later
#include <iostream>
int main()
{
// Show that a istream_iterator has an input_iterator
std::cout << std::boolalpha << std::input_iterator<std::istream_iterator<int>>; // outputs true
}
input_or_output_iterator
반복 input_or_output_iterator
기 개념 분류의 기초입니다. 반복기를 역참조 및 증가시키는 것을 지원합니다. 모든 반복기 모델 input_or_output_iterator
.
template<class I>
concept input_or_output_iterator =
requires(I i) {
{ *i } -> can-reference;
} &&
weakly_incrementable<I>;
매개 변수
I
테스트 input_or_output_iterator
할 형식입니다.
설명
이 개념은 can-reference
형식 I
이 참조, 포인터 또는 참조로 암시적으로 변환될 수 있는 형식임을 의미합니다.
예: input_or_output_iterator
다음 예제에서는 개념을 사용하여 input_or_output_iterator
다음이 있음을 vector<int>
표시합니다.input_or_output_iterator
// requires /std:c++20 or later
#include <iostream>
int main()
{
// Show that a vector has an input_or_output_iterator
std::cout << std::boolalpha << std::input_or_output_iterator<std::vector<int>::iterator> << '\n'; // outputs true
// another way to test
std::vector<int> v = {0,1,2};
std::cout << std::boolalpha << std::input_or_output_iterator<decltype(v)::iterator>; // outputs true
}
output_iterator
작성 output_iterator
할 수 있는 반복기입니다.
template<class I, class T>
concept output_iterator =
input_or_output_iterator<I> &&
indirectly_writable<I, T> &&
requires(I i, T&& t) {
*i++ = std::forward<T>(t);
};
매개 변수
I
테스트 output_iterator
할 형식입니다.
T
쓸 값의 형식입니다.
설명
단일 output_iterator
패스입니다. 즉, 동일한 요소에 한 번만 쓸 수 있습니다.
예: output_iterator
다음 예제에서는 개념을 사용하여 output_iterator
다음이 있음을 vector<int>
표시합니다.output_iterator
// requires /std:c++20 or later
#include <iostream>
#include <vector>
int main()
{
// Show that vector<int> has an output_iterator
std::cout << std::boolalpha << std::output_iterator<std::vector<int>::iterator, int> << "\n"; // outputs "true"
// another way to test
std::vector<int> v = {0,1,2,3,4,5};
std::cout << std::boolalpha << std::output_iterator<decltype(v)::iterator, int>; // outputs true
}
random_access_iterator
A는 random_access_iterator
인덱스로 읽거나 쓸 수 있습니다.
template<class I>
concept random_access_iterator =
bidirectional_iterator<I> &&
derived_from<ITER_CONCEPT(I), random_access_iterator_tag> &&
totally_ordered<I> &&
sized_sentinel_for<I, I> &&
requires(I i, const I j, const iter_difference_t<I> n) {
{ i += n } -> same_as<I&>;
{ j + n } -> same_as<I>;
{ n + j } -> same_as<I>;
{ i -= n } -> same_as<I&>;
{ j - n } -> same_as<I>;
{ j[n] } -> same_as<iter_reference_t<I>>;
};
매개 변수
I
테스트할 형식이 .인지 확인합니다 random_access_iterator
.
설명
A random_access_iterator
에는 , output_iterator
, forward_iterator
및 bidirectional_iterator
.input_iterator
의 몇 가지 예는 random_access_iterator
vector
, array
및 deque
.
예: random_access_iterator
다음 예제에서는 a에 vector<int>
다음이 있음을 보여줍니다.random_access_iterator
// requires /std:c++20 or later
#include <iostream>
#include <vector>
int main()
{
// Show that vector<int> has a random_access_iterator
std::cout << std::boolalpha << std::random_access_iterator<std::vector<int>::iterator> << '\n'; // outputs "true"
// another way to test
std::vector<int> v = {0,1,2};
std::cout << std::boolalpha << std::random_access_iterator<decltype(v)::iterator>; // outputs true
}
sentinel_for
형식이 반복기의 sentinel임을 지정합니다.
template<class S, class I>
concept sentinel_for =
semiregular<S> &&
input_or_output_iterator<I> &&
weakly-equality-comparable-with <S, I>;
매개 변수
I
반복기 형식입니다.
S
에 대한 I
sentinel인지 확인하기 위해 테스트할 형식입니다.
설명
sentinel은 반복기가 끝에 도달했는지 확인하기 위해 반복기와 비교할 수 있는 형식입니다. 이 개념은 형식이 , output_iterator
,forward_iterator
bidirectional_iterator
random_access_iterator
, 및 contiguous_iterator
를 포함하는 input_iterator
형식 중 input_or_output_iterator
하나에 대한 sentinel인지 여부를 결정합니다.
예: sentinel_for
다음 예제에서는 개념을 사용하여 sentinel_for
다음의 센티넬임을 vector<int>::iterator
표시합니다 vector<int>
.
// requires /std:c++20 or later
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v = {0, 1, 2};
std::vector<int>::iterator i = v.begin();
// show that vector<int>::iterator is a sentinel for vector<int>
std::cout << std::boolalpha << std::sentinel_for<std::vector<int>::iterator, decltype(i)>; // outputs true
}
sized_sentinel_for
반복기와 해당 sentinel을 빼 -
서 일정한 시간에 차이를 찾을 수 있는지 테스트합니다.
template<class S, class I>
concept sized_sentinel_for =
sentinel_for<S, I> &&
!disable_sized_sentinel_for<remove_cv_t<S>, remove_cv_t<I>> &&
requires(const I& i, const S& s) {
{s - i} -> same_as<iter_difference_t<I>>;
{i - s} -> same_as<iter_difference_t<I>>;
};
매개 변수
I
반복기 형식입니다.
S
테스트할 sentinel 형식입니다.
설명
예: sized_sentinel_for
다음 예제에서는 이 개념을 사용하여 sized_sentinel_for
일정한 시간에 벡터 반복기에서 a의 sentinel vector<int>
을 뺄 수 있는지 확인합니다.
// requires /std:c++20 or later
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v = { 1, 2, 3 };
std::vector<int>::iterator i = v.begin();
std::vector<int>::iterator end = v.end();
// use the sized_sentinel_for concept to verify that i can be subtracted from end in constant time
std::cout << std::boolalpha << std::sized_sentinel_for<decltype(end), decltype(i)> << "\n"; // outputs true
std::cout << end - i; // outputs 3
}