Диапазоны и представления (C++20)
Компонуемые, ленивые конвейеры над вашими данными
<ranges>) позволяет описать преобразование данных как конвейер из небольших компонуемых шагов, соединённых оператором конвейера |. Каждый шаг — это представление (view): лёгкий, не владеющий данными объект, который описывает, как получать элементы, и вычисляет их лениво — по одному, только когда вы выполняете обход. Вы уже знаете контейнеры STL, итераторы, алгоритмы и лямбды; диапазоны склеивают их в читаемые конвейеры и убирают шумные пары begin()/end().
- объяснить, что такое диапазон и представление и почему представления дёшевы и ленивы,
- строить конвейеры с помощью адаптеров диапазонов, таких как
filter,transform,takeиdrop, - использовать проекцию, чтобы направить алгоритм диапазонов на одно поле объекта,
- рассуждать о владении и времени жизни, чтобы избегать «висячих» представлений.
1. От пар итераторов к диапазонам
Классические алгоритмы STL принимают пару итераторов. Чтобы отфильтровать, преобразовать и напечатать контейнер, вы жонглируете begin()/end(), промежуточными контейнерами и написанными вручную циклами:
// классический C++17: три прохода, один временный вектор
std::vector<int> evens;
std::copy_if(v.begin(), v.end(), std::back_inserter(evens),
[](int n){ return n % 2 == 0; });
std::vector<int> squared;
std::transform(evens.begin(), evens.end(), std::back_inserter(squared),
[](int n){ return n * n; });
Диапазон — это просто всё, что можно обойти: он предоставляет begin() и end(). Каждый стандартный контейнер является диапазоном. C++20 добавляет ограниченные алгоритмы в пространстве имён std::ranges, которые принимают целый диапазон одним аргументом, так что пара исчезает:
std::ranges::sort(v); // вместо sort(v.begin(), v.end())
auto n = std::ranges::count(v, 42);
Ещё больший выигрыш даёт часть библиотеки, посвящённая представлениям, которая превращает эти два ручных цикла в единый декларативный конвейер.