<ranges>
Классы представления
Представление — это упрощенный диапазон, который относится к элементам, которым он не владеет (за исключениемowning_view
). Представление обычно основано на другом диапазоне и предоставляет другой способ просмотра, будь то преобразование или фильтрация. Например, это представление, std::views::filter
использующее критерии, заданные для выбора элементов из другого диапазона.
При доступе к элементам в представлении выполняется "лениво" так, чтобы работа выполнялось только при получении элемента. Это позволяет объединять или создавать представления без штрафа за производительность.
Например, можно создать представление, которое предоставляет только те же элементы из диапазона, а затем преобразовать их путем их выравнивания. Работа по фильтрации и преобразованию выполняется только для элементов, к которым вы обращаетесь, и только при доступе к ним.
Представление можно скопировать, назначить и уничтожить в постоянном времени независимо от количества элементов, содержащихся в нем. Это связано с тем, что представление не владеет элементами, на которые он ссылается, поэтому не требуется создавать копию. Именно поэтому вы можете создавать представления без штрафа производительности.
Обычно представление создается с помощью адаптера диапазона. Адаптеры диапазона предназначены для создания представления, проще использовать, чем создавать экземпляры классов представления напрямую, а иногда более эффективными, чем создание экземпляров классов представления напрямую. Классы представления предоставляются непосредственно в случае, если необходимо создать собственный тип пользовательского представления на основе существующего типа представления.
Ниже приведен краткий пример создания представления квадратов элементов, делимых тремя в векторе:
// requires /std:c++20 or later
#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 << ' '; // 0 9 36 81
}
}
0 9 36 81
Использование представления после изменения диапазона может привести к неопределенному поведению. Например, при reverse_view
добавлении или удалении элементов из базового вектора не следует повторно использовать вектор. Изменение базового вектора делает итератор контейнера end
недействительным, включая копию итератора, которую может сделать представление.
Так как представления являются дешевыми для создания, обычно следует повторно создать представление, если изменить базовый диапазон. В следующем примере показано, как сохранить конвейер представления в переменной, чтобы его можно было повторно использовать.
// requires /std:c++20, or later
#include <iostream>
#include <ranges>
#include <vector>
#include <list>
#include <string_view>
#include <algorithm>
template<typename rangeType>
void show(std::string_view msg, rangeType r)
{
std::cout << msg;
std::ranges::for_each(r,
[](auto e)
{
std::cout << e << ' ';
});
std::cout << '\n';
}
int main()
{
std::vector v{ 1, 2, 3, 4 };
show("v: ", v);
// You can save a view pipeline
auto rev3 = std::views::take(3) | std::views::reverse;
show("v | rev3: ", v | rev3); // 3 2 1
v.insert(v.begin(), 0); // v = 0 1 2 3 4
show("v: ", v);
// Because modifying the vector invalidates its iterators, rebuild the view.
// We are reusing the view pipeline we saved earlier
show("v | rev3(v): ", rev3(v));
}
v: 1 2 3 4
v | rev3: 3 2 1
v: 0 1 2 3 4
v | rev3(v): 2 1 0
В пространстве имен определены следующие классы представлений std::ranges
.
Представления | Description |
---|---|
basic_istream_view C++20 |
Представление последовательных элементов из входного потока. К специализациям относятся istream_view и wistream_view . |
common_view C++20 |
Адаптирует представление с различными типами итератора или sentinel в представление с одинаковыми типами итератора или sentinel. |
drop_view C++20 |
Создано из другого представления, пропуская первые count элементы. |
drop_while_view C++20 |
Создано из другого представления, пропуская ведущие элементы до тех пор, пока предикат удерживает. |
elements_view C++20 |
Представление выбранного индекса в каждом кортеже, как значение в коллекции. Например, учитывая диапазон значений std::tuple<string, int> , создайте представление, состоящее из всех string элементов из каждого кортежа. |
empty_view C++20 |
Представление без элементов. |
filter_view C++20 |
Фильтрует элементы диапазона, которые не соответствуют предикату. |
iota_view C++20 |
Созданное представление, содержащее последовательность добавочных значений. |
join_view C++20 |
Объединяет все элементы нескольких диапазонов в одно представление. |
keys_view C++20 |
Представление по первому индексу в каждом кортеже, как значение в коллекции. Например, учитывая диапазон значений std::tuple<string, int> , создайте представление, состоящее из string элементов из каждого кортежа. |
lazy_split_view C++20 |
Разбивает представление на подранги на основе разделителя. |
owning_view C++20 |
Владеет элементами из другого диапазона. |
ref_view C++20 |
Представление, ссылающееся на элементы, принадлежащие другому диапазону. |
reverse_view C++20 |
Представляет элементы диапазона в обратном порядке. |
single_view C++20 |
Представление, содержащее только один элемент. |
split_view C++20 |
Разбивает представление на подранги на основе разделителя. |
subrange C++20 |
Представление части элементов диапазона, как определено начальным итератором и sentinel. |
take_view C++20 |
Содержит указанное количество элементов, взятых с передней части диапазона. |
take_while_view C++20 |
Содержит ведущие элементы диапазона, которые соответствуют заданному предикату. |
transform_view C++20 |
Представление базовой последовательности после применения функции преобразования к каждому элементу. |
values_view C++20 |
Представление по второму индексу в каждом кортеже, как значение в коллекции. Например, учитывая диапазон значений std::tuple<string, int> , создайте представление, состоящее из int элементов из каждого кортежа. |
Многие из этих классов имеют соответствующие адаптеры диапазона в std:views
пространстве имен, которое создает экземпляры. Предпочитайте использовать адаптер для создания представления вместо создания классов представлений напрямую. Адаптеры диапазона предназначены для создания представлений, проще использовать, а в некоторых случаях — более эффективным.
Просмотр характеристик классов
Каждый раздел класса представления содержит раздел "Характеристики " после раздела синтаксиса. Раздел "Характеристики" содержит следующие записи:
Адаптер диапазона: ссылка на адаптер диапазона, создающий представление. Обычно для создания представления используется адаптер диапазона, а не непосредственно для создания класса представления, поэтому он указан здесь для удобства.
Базовый диапазон: представления имеют разные требования итератора для типа базового диапазона, который они могут использовать. Дополнительные сведения о типах итераторов см . в иерархии итераторов диапазонов.
Просмотр категории итератора: категория итератора представления. Когда представление адаптирует диапазон, тип итератора для представления обычно совпадает с типом итератора базового диапазона. Однако для некоторых представлений это может отличаться. Например, имеет значение ,
reverse_view
даже если базовыйbidirectional_iterator
диапазон имеет .random_access_iterator
Тип элемента: тип элементов, возвращаемых итератором представления.
Размер: может ли представление возвращать количество элементов, на которые он ссылается. Не все представления могут.
Общий диапазон: указывает, является ли представление одинаковым
common_range
, то есть начальный итератор и типы sentinel. Общие диапазоны полезны для кода предопределенного диапазона, который работает с парами итератора. Примером являются конструкторы пар итератора для контейнера последовательности, напримерvector(ranges::begin(x), ranges::end(x))
.Заимствуемый диапазон: указывает, является ли представление заимствованным диапазоном.
borrowed_range<T>
означает, что после уничтожения можно использовать итераторыT
T
.Стандартный контейнер не является заимствованным диапазоном, так как уничтожение контейнера освобождает элементы и делает его недействительными. В этом случае мы говорим, что итераторы остаются "жалкими" после уничтожения.
Например,
std::ranges::find()
обычно возвращает итератор для найденного элемента в аргументе диапазона. Если аргумент диапазона является временным контейнером (rvalue), это ошибка, чтобы сохранить возвращенный итератор и использовать его позже, так как это "dangling".Алгоритмы диапазона, возвращающие итераторы (или подранги), делают это только в том случае, если их аргументы являются lvalues (не временными) или заимствованными диапазонами. В противном случае они возвращают
std::dangling
объект, который предоставляет подсказку в сообщениях об ошибках о том, что пошло не так, если вы попытались использовать его как итератор.Является
const
итерируемым: указывает, можно ли выполнять итерацию поconst
экземпляру представления. Не всеconst
представления можно итерировать. Если представление неconst
является итерируемым, вы не можете выполнить итерацию илиfor (const auto& element : as_const(theView))
передать ее в функцию, которая принимаетconst
ссылку на представление, а затем пытается выполнить итерацию по нему.
Иерархия итератора диапазонов
В разделе "Характеристики" каждого раздела класса представления категория итератора в базовом диапазоне и категория "Просмотр итератора" относится к типу итератора, который поддерживает диапазон или представление. Существует шесть категорий итераторов Ranges, которые определяются концепциями C++20. Иерархия итераторов диапазона в порядке увеличения возможностей:
Концепция итератора диапазона | Description |
---|---|
output_range |
Только запись, только перемещается вперед; одиночный проход. |
input_range |
Только для чтения, только перемещается вперед; одиночный проход. |
forward_range |
Только движется вперед; с несколькими проходами. |
bidirectional_range |
Может двигаться вперед и назад; с несколькими проходами. |
random_access_range |
Доступ к коллекции с помощью индекса; с несколькими проходами. |
contiguous_range |
Может получить доступ к коллекции с индексом, а элементы хранятся в памяти непрерывно. |
Как правило, итератор имеет возможность итераторов, предшествующих ей в таблице. Например, bidirectional_range
имеет возможности forward_range
, но не наоборот. За исключением input_range
того, что у вас нет возможности output_range
, так как вы не можете написать в нее input_range
.
Оператор "требует input_range
или выше" означает, что представление можно использовать с итератором input_range
, bidirectional_range
forward_range
random_access_range
или contiguous_range
итератором, так как они все как способныinput_range
.
Иерархия итератора диапазонов напрямую связана с иерархией итератора. Дополнительные сведения см. в концепциях итератора.