Advanced Ranges and Views
Advanced Ranges and Views
Build readable lazy pipelines and understand when to keep views versus materializing results.
Advanced Ranges and Views
Build readable lazy pipelines and understand when to keep views versus materializing results.
Ranges let you express data flow directly: filter, transform, slice, then consume. This often reads closer to the problem than manual iterator code.
auto ids = users
| std::views::filter([](const User& user) { return user.active; })
| std::views::transform([](const User& user) { return user.id; });
The pipeline above is lazy. It does not create a new container automatically.
That is useful when:
Materialize into a container when you need stable ownership or repeated traversal.
std::ranges::sort(users, std::greater<>{}, &User::score);
This sorts by score without writing a custom comparator body.
std::vector<std::string> top_names(const std::vector<User>& users) {
auto view = users
| std::views::filter([](const User& user) { return user.score >= 90; })
| std::views::transform([](const User& user) { return user.name; });
return {view.begin(), view.end()};
}
struct Event {
std::string type;
int duration_ms{};
bool success{};
};
std::vector<std::string> slow_successes(const std::vector<Event>& events) {
auto view = events
| std::views::filter([](const Event& event) {
return event.success && event.duration_ms > 100;
})
| std::views::transform([](const Event& event) {
return std::format("{}:{}ms", event.type, event.duration_ms);
});
return {view.begin(), view.end()};
}
This is the shape ranges are best at: filtering, transforming, and only materializing when you finally need owned results.
Materialize when one of these becomes true:
std::vector<std::string> cached(view.begin(), view.end());
That one line makes the ownership change explicit.
Views are powerful, but readability still wins. If a pipeline becomes dense, split it.
#include <algorithm>
#include <vector>
int main() {
std::vector<int> values{4, 1, 3, 2};
std::sort(values.begin(), values.end());
return std::binary_search(values.begin(), values.end(), 3) ? 0 : 1;
}
Replace the hand-written search loop with a ranges pipeline. It highlights when composition improves clarity and when it becomes too indirect.
#include <ranges>
#include <vector>
int main() {
std::vector<int> values{1, 2, 3, 4, 5, 6};
auto odd = values | std::views::filter([](int value) { return value % 2 == 1; });
return *odd.begin();
}