C++20 Ranges and Views
Frequently Asked Questions
QCan views be used with plain C arrays?
QAre views always lazy?
QHow do I materialize a view into a concrete container?
QHow does the ranges library protect against dangling iterators?
#include <iostream>
#include <format>
#include <vector>
#include <string>
#include <algorithm>
#include <ranges>int main() {1 Range-based algorithms (no begin/end needed!)
Ranges compose algorithms and views into lazy, pipeline-style transformations.
Use them for readable data-processing chains over containers and iterables.
They reduce temporary containers and improve expression-level clarity.
Compose with `|` and call range algorithms on views/containers.
std::ranges::sort replaces std::sort(v.begin(), v.end())
std:: algorithms are different overloads. Do not mix their iterator types in one call.
Watch out: std::ranges:: algorithms and
std::cout << "--- Range Algorithms ---\n";
std::vector<int> nums = {5, 2, 8, 1, 9, 3, 7};
std::ranges::sort(nums); // Clean! No begin/end
std::cout << "Sorted: ";
for (int n : nums) std::cout << n << ' ';
std::cout << '\n';
// ranges::find returns a sentinel-aware iterator
if (auto it = std::ranges::find(nums, 7); it != nums.end()) {
std::cout << std::format("Found 7 at index {}\n",
std::distance(nums.begin(), it));
}
// ranges::count, ranges::min, ranges::max
std::cout << std::format("Min: {}, Max: {}\n",
std::ranges::min(nums),
std::ranges::max(nums));2 Views: lazy, composable transformations
Views: lazy, composable transformations.
Use this when it cleanly solves the problem in front of you.
Views don't modify the original data.
Use it in code like: std::cout << "\n--- Views ---\n";.
Views don't modify the original data. They are evaluated on-demand (lazy).
std::cout << "\n--- Views ---\n";
// filter: keep only even numbers
std::cout << "Even numbers: ";
for (int n : nums | std::views::filter([](int n) { return n % 2 == 0; })) {
std::cout << n << ' ';
}
std::cout << '\n';
// transform: square each element
std::cout << "Squared: ";
for (int n : nums | std::views::transform([](int n) { return n * n; })) {
std::cout << n << ' ';
}
std::cout << '\n';
// take: only the first N elements
std::cout << "First 3: ";
for (int n : nums | std::views::take(3)) {
std::cout << n << ' ';
}
std::cout << '\n';
// drop: skip the first N elements
std::cout << "Skip 4: ";
for (int n : nums | std::views::drop(4)) {
std::cout << n << ' ';
}
std::cout << '\n';
// reverse
std::cout << "Reversed: ";
for (int n : nums | std::views::reverse) {
std::cout << n << ' ';
}
std::cout << '\n';3 Composing views with the pipe operator
Ranges compose algorithms and views into lazy, pipeline-style transformations.
Use them for readable data-processing chains over containers and iterables.
They reduce temporary containers and improve expression-level clarity.
Compose with `|` and call range algorithms on views/containers.
This is the real power of ranges: chain multiple transformations, evaluated lazily.
std::cout << "\n--- Composing Views ---\n";
// Get squares of even numbers, take first 2
auto pipeline = nums
| std::views::filter([](int n) { return n % 2 == 0; })
| std::views::transform([](int n) { return n * n; })
| std::views::take(2);
std::cout << "Squares of evens (first 2): ";
for (int n : pipeline) {
std::cout << n << ' ';
}
std::cout << '\n';4 views::iota -- generate a range of numbers
Ranges compose algorithms and views into lazy, pipeline-style transformations.
Use them for readable data-processing chains over containers and iterables.
They reduce temporary containers and improve expression-level clarity.
Compose with `|` and call range algorithms on views/containers.
std::cout << "\n--- views::iota ---\n";
// Numbers 1 to 10
std::cout << "1..10: ";
for (int n : std::views::iota(1, 11)) {
std::cout << n << ' ';
}
std::cout << '\n';
// FizzBuzz with ranges!
std::cout << "\nFizzBuzz (1-15):\n";
for (int n : std::views::iota(1, 16)) {
if (n % 15 == 0) std::cout << "FizzBuzz ";
else if (n % 3 == 0) std::cout << "Fizz ";
else if (n % 5 == 0) std::cout << "Buzz ";
else std::cout << std::format("{} ", n);
}
std::cout << '\n';5 Projections: transform the comparison key
A lambda defines an unnamed callable object directly at the use site.
Use lambdas for short callbacks, predicates, and local behavior.
They keep behavior close to the call site and avoid extra named functor types.
Write [captures](params) { body } and keep captured state explicit.
Sort strings by length without a custom comparator.
Projections are a major ergonomic win over classic STL: instead of writing a lambda comparator, pass a member pointer or callable as the third argument to ranges:: algorithms.
std::cout << "\n--- Projections ---\n";
std::vector<std::string> words = {"banana", "fig", "cherry", "apple", "date"};
// Sort by string length using a projection
std::ranges::sort(words, {}, &std::string::size);
std::cout << "Sorted by length: ";
for (const auto& w : words) {
std::cout << std::format("{}({}) ", w, w.size());
}
std::cout << '\n';
return 0;
}