Ranges and Views (C++20)
Composable, lazy pipelines over your data
<ranges>) lets you describe a data transformation as a pipeline of small, composable steps joined with the pipe operator |. Each step is a view: a lightweight, non-owning object that describes how to produce elements and computes them lazily — one at a time, only when you iterate. You already know STL containers, iterators, algorithms and lambdas; ranges glue them into readable pipelines and remove the noisy begin()/end() pairs.
- explain what a range and a view are, and why views are cheap and lazy,
- build pipelines with range adaptors such as
filter,transform,takeanddrop, - use a projection to point a ranges algorithm at one field of an object,
- reason about ownership and lifetime so you avoid dangling views.
1. From iterator pairs to ranges
Classic STL algorithms take a pair of iterators. To filter, transform and print a container you juggle begin()/end(), intermediate containers and hand-written loops:
// classic C++17: three passes, one temporary vector
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; });
A range is simply anything you can iterate: it exposes begin() and end(). Every standard container is a range. C++20 adds constrained algorithms in the std::ranges namespace that accept a whole range in one argument, so the pair disappears:
std::ranges::sort(v); // instead of sort(v.begin(), v.end())
auto n = std::ranges::count(v, 42);
The bigger win is the views half of the library, which turns those two hand-written loops into a single declarative pipeline.