Ranges and Views
C++20's ranges library modernises the algorithms: you pass whole containers instead of iterator pairs, and views let you build lazy pipelines — filter, transform, take — with the | operator, without creating intermediate containers.
Range algorithms
Every classic algorithm has a std::ranges:: version that takes the container itself. They also accept a projection — a member or function to apply before comparing:
#include <algorithm> #include <iostream> #include <string> #include <vector> struct Employee { std::string name; int age; }; int main() { std::vector<int> v{5, 2, 9, 1}; std::ranges::sort(v); std::cout << std::ranges::max(v) << ' ' << std::ranges::count(v, 9) << '\n'; std::vector<Employee> staff{{"Ana", 31}, {"Ben", 27}, {"Chloe", 35}}; std::ranges::sort(staff, {}, &Employee::age); // sort by age (projection) for (const auto& e : staff) std::cout << e.name << ' '; std::cout << '\n'; auto it = std::ranges::find(staff, "Chloe", &Employee::name); std::cout << it->name << " is " << it->age << '\n'; }
9 1 Ben Ana Chloe Chloe is 35
The {} means "use the default comparison"; &Employee::age tells the algorithm to compare ages.
Views and pipelines
A view is a lightweight, lazy adaptor over a range. Chaining views with | reads left to right like a recipe:
#include <iostream> #include <ranges> #include <vector> int main() { std::vector<int> numbers{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; auto even_squares = numbers | std::views::filter([](int n) { return n % 2 == 0; }) | std::views::transform([](int n) { return n * n; }); for (int x : even_squares) std::cout << x << ' '; std::cout << '\n'; }
4 16 36 64 100
Nothing is computed when even_squares is created. Each value is produced on demand as the loop asks for it, and no temporary vectors are made.
Useful views
| View | Produces |
|---|---|
views::filter(pred) | Elements for which pred is true |
views::transform(f) | f(element) for each element |
views::take(n), views::drop(n) | The first n / all but the first n |
views::take_while(pred), drop_while | Until / after a condition fails |
views::reverse | Reverse order |
views::iota(a, b) | The integers a, a+1, …, b−1 (or forever with one argument) |
views::keys, views::values | The first / second parts of pairs (e.g. map entries) |
views::split(sep) | Sub-ranges between separators |
#include <iostream> #include <map> #include <ranges> #include <string> int main() { for (int n : std::views::iota(1) | std::views::filter([](int n) { return n % 7 == 0; }) | std::views::take(5)) { std::cout << n << ' '; } std::cout << '\n'; std::map<std::string, int> stock{{"apples", 30}, {"pears", 0}, {"plums", 12}}; for (const auto& name : stock | std::views::keys) std::cout << name << ' '; std::cout << '\n'; for (int v : stock | std::views::values | std::views::reverse) std::cout << v << ' '; std::cout << '\n'; }
7 14 21 28 35 apples pears plums 12 0 30
views::iota(1) is an infinite sequence — laziness makes that fine, because take(5) stops it.
Views refer to their source
A view doesn't own data; it looks at the underlying container. Changing the container changes what the view shows, and a view must not outlive its container. To keep the results, copy them into a container:
#include <iostream> #include <ranges> #include <string> #include <vector> int main() { std::vector<std::string> words{"apple", "fig", "banana", "kiwi"}; auto long_words = words | std::views::filter([](const std::string& w) { return w.size() > 4; }); std::vector<std::string> kept(long_words.begin(), long_words.end()); for (const auto& w : kept) std::cout << w << ' '; std::cout << '\n'; }
apple banana
C++23 adds std::ranges::to<std::vector>() to do this conversion at the end of a pipeline (lesson 50).
Ranges vs. loops
A pipeline is great when it reads like a description of the result. For complicated logic with several conditions and side effects, a plain loop is often clearer. Use whichever communicates better — both compile to similarly efficient code.
Try it yourself
From a list of temperatures, print the first three readings above 20 degrees, converted to Fahrenheit.
69.8 74.3 77