C++20 Ranges and Views
Prerequisites: Iterators, lambdas, STL algorithm basics
Standard: C++20 (headers <algorithm>, <ranges>)
int main() {1. Range-based algorithms (no begin/end needed!)
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 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
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
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
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;
}