Поделиться через


<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_viewC++20 Представление последовательных элементов из входного потока. К специализациям относятся istream_view и wistream_view.
common_viewC++20 Адаптирует представление с различными типами итератора или sentinel в представление с одинаковыми типами итератора или sentinel.
drop_viewC++20 Создано из другого представления, пропуская первые count элементы.
drop_while_viewC++20 Создано из другого представления, пропуская ведущие элементы до тех пор, пока предикат удерживает.
elements_viewC++20 Представление выбранного индекса в каждом кортеже, как значение в коллекции. Например, учитывая диапазон значений std::tuple<string, int> , создайте представление, состоящее из всех string элементов из каждого кортежа.
empty_viewC++20 Представление без элементов.
filter_viewC++20 Фильтрует элементы диапазона, которые не соответствуют предикату.
iota_viewC++20 Созданное представление, содержащее последовательность добавочных значений.
join_viewC++20 Объединяет все элементы нескольких диапазонов в одно представление.
keys_viewC++20 Представление по первому индексу в каждом кортеже, как значение в коллекции. Например, учитывая диапазон значений std::tuple<string, int> , создайте представление, состоящее из string элементов из каждого кортежа.
lazy_split_viewC++20 Разбивает представление на подранги на основе разделителя.
owning_viewC++20 Владеет элементами из другого диапазона.
ref_viewC++20 Представление, ссылающееся на элементы, принадлежащие другому диапазону.
reverse_viewC++20 Представляет элементы диапазона в обратном порядке.
single_viewC++20 Представление, содержащее только один элемент.
split_viewC++20 Разбивает представление на подранги на основе разделителя.
subrangeC++20 Представление части элементов диапазона, как определено начальным итератором и sentinel.
take_viewC++20 Содержит указанное количество элементов, взятых с передней части диапазона.
take_while_viewC++20 Содержит ведущие элементы диапазона, которые соответствуют заданному предикату.
transform_viewC++20 Представление базовой последовательности после применения функции преобразования к каждому элементу.
values_viewC++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_rangeforward_rangerandom_access_rangeили contiguous_range итератором, так как они все как способныinput_range.

Иерархия итератора диапазонов напрямую связана с иерархией итератора. Дополнительные сведения см. в концепциях итератора.

См. также

<ranges>
Адаптеры диапазона