Основные понятия итератора
Основные понятия — это функция языка 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 или более поздней версии ошибка, которая оценивается как false, указывает непосредственно на требование dividable<char*>
(a / b)
выражения, которое завершилось сбоем.
Основные понятия итератора определяются в std
пространстве имен и объявляются в файле заголовка <iterator>
. Они используются в объявлениях адаптеров диапазона, представлений и т. д.
Существует шесть категорий итераторов. Они напрямую связаны с категориями диапазонов, перечисленных в разделе "Основные понятия диапазона".
Следующие понятия итератора перечислены в порядке увеличения возможностей. input_or_output_iterator
находится в нижней части иерархии возможностей и contiguous_iterator
находится на высоком уровне. Итераторы выше в иерархии, как правило, можно использовать вместо тех, которые ниже, но не наоборот. Например, random_access_iterator
итератор можно использовать вместо forward_iterator
итератора, но не наоборот. Исключение — это input_iterator
исключение, которое нельзя использовать вместо output_iterator
нее, так как он не может записываться.
В следующей таблице "Multi-pass" указывает, может ли итератор повторно вернуть один и тот же элемент несколько раз. Например, vector::iterator
является многоадровным итератором, так как вы можете сделать копию итератора, считывать элементы в коллекции, а затем восстанавливать итератор до значения в копии, а затем повторно возвращать те же элементы. Если итератор является однопроходным, вы можете посетить только элементы коллекции один раз.
В следующей таблице "Примеры типов" относится к коллекциям и итераторам, удовлетворяющим концепции.
Концепция итератора | Description | Направление | Чтение/запись | Многопроходная передача | Примеры типов |
---|---|---|---|---|---|
input_or_output_iterator C++20 |
Основа итеромии концепции таксономии. | Переслать | Чтение/запись | no | istream_iterator , ostream_iterator |
output_iterator C++20 |
Указывает итератор, в который можно написать. | Переслать | Write | no | ostream , inserter |
input_iterator C++20 |
Указывает итератор, который можно прочитать один раз. | Переслать | Читать | no | istream , istreambuf_iterator |
forward_iterator C++20 |
Указывает итератор, который может читать (и, возможно, записывать) несколько раз. | Переслать | Чтение/запись | yes | vector , list |
bidirectional_iterator C++20 |
Указывает итератор, который можно читать и записывать как вперед, так и назад. | Вперед или назад | Чтение/запись | yes | list , set , multiset , map и multimap . |
random_access_iterator C++20 |
Указывает итератор, который можно читать и записывать по индексу. | Вперед или назад | Чтение/запись | yes | vector , , array deque |
contiguous_iterator C++20 |
Указывает итератор, элементы которого последовательно находятся в памяти, имеют одинаковый размер и могут быть доступны с помощью арифметики указателя. | Вперед или назад | Чтение/запись | yes | array , vector string . |
К другим понятиям итератора относятся:
Концепция итератора | Description |
---|---|
sentinel_for C++20 |
Указывает, что тип является sentinel для типа итератора. |
sized_sentinel_for C++20 |
Указывает, что итератор и его sentinel можно вычитать (используя - ), чтобы найти их разницу в постоянном времени. |
bidirectional_iterator
Поддерживает 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
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
ли это .
Замечания
Доступ 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
ли это .
Замечания
Может 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
Объект 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
имеет возможности input_iterator
, и output_iterator
forward_iterator
bidirectional_iterator
.
Ниже приведены некоторые примеры random_access_iterator
vector
: , array
и deque
.
Пример: random_access_iterator
В следующем примере показано, что у 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
Тип для проверки, чтобы узнать, является ли это sentinel для I
.
Замечания
Sentinel — это тип, который можно сравнить с итератором, чтобы определить, достиг ли итератор. Эта концепция определяет, является ли тип sentinel для одного из input_or_output_iterator
типов, включая input_iterator
, , output_iterator
, forward_iterator
bidirectional_iterator
и .contiguous_iterator
random_access_iterator
Пример: sentinel_for
В следующем примере используется концепция, показывающая sentinel_for
, что vector<int>::iterator
это sentinel для 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
концепция для проверки того, что 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
}
См. также
Основные понятия диапазона
Адаптеры диапазона
Просмотр классов