Search Knowledge

© 2026 LIBREUNI PROJECT

Modern C++ Programming / Modern C++20/23

Ranges Library (C++20)

Ranges: Composable Algorithms

C++20 ranges revolutionize how we work with sequences, enabling composable, lazy-evaluated operations with cleaner syntax.

Range-Based Algorithms

#include <ranges>
#include <algorithm>
namespace rng = std::ranges;

std::vector<int> vec = {5, 2, 8, 1, 9, 3};

// Old style
std::sort(vec.begin(), vec.end());

// Range style
rng::sort(vec);  // Operates on entire range

// With projection
struct Person { std::string name; int age; };
std::vector<Person> people = /*...*/;

rng::sort(people, {}, &Person::age);  // Sort by age

Views: Lazy Evaluation

Views are lightweight range adaptors that don’t own data and evaluate lazily:

#include <ranges>
namespace views = std::views;

std::vector<int> vec = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

// filter: Select elements matching predicate
auto even = vec | views::filter([](int x) { return x % 2 == 0; });
// No computation yet - lazy!

// transform: Apply function to each element
auto squared = vec | views::transform([](int x) { return x * x; });

// take: First n elements
auto first_three = vec | views::take(3);

// drop: Skip first n elements
auto skip_two = vec | views::drop(2);

Composing Views

Views compose using the pipe operator:

auto result = vec
    | views::filter([](int x) { return x % 2 == 0; })
    | views::transform([](int x) { return x * x; })
    | views::take(3);

for (int x : result) {
    std::cout << x << ' ';  // 4 16 36
}
// Computation happens here during iteration

Common Views

views::iota

Generate sequence of numbers:

// Infinite sequence: 1, 2, 3, ...
for (int i : views::iota(1) | views::take(5)) {
    std::cout << i << ' ';  // 1 2 3 4 5
}

// Bounded sequence
for (int i : views::iota(1, 6)) {
    std::cout << i << ' ';  // 1 2 3 4 5
}

views::reverse

std::vector<int> vec = {1, 2, 3, 4, 5};

for (int x : vec | views::reverse) {
    std::cout << x << ' ';  // 5 4 3 2 1
}

views::keys and views::values

For associative containers:

std::map<std::string, int> map = {{"a", 1}, {"b", 2}, {"c", 3}};

for (const auto& key : map | views::keys) {
    std::cout << key << ' ';  // a b c
}

for (int value : map | views::values) {
    std::cout << value << ' ';  // 1 2 3
}

views::split

std::string str = "hello,world,test";

for (auto word : str | views::split(',')) {
    for (char c : word) {
        std::cout << c;
    }
    std::cout << '\\n';
}
// hello
// world
// test

views::join

Flatten range of ranges:

std::vector<std::vector<int>> nested = {{1, 2}, {3, 4}, {5, 6}};

for (int x : nested | views::join) {
    std::cout << x << ' ';  // 1 2 3 4 5 6
}

views::zip (C++23)

Combine multiple ranges:

std::vector<int> nums = {1, 2, 3};
std::vector<std::string> words = {"one", "two", "three"};

for (auto [num, word] : views::zip(nums, words)) {
    std::cout << num << ": " << word << '\\n';
}
// 1: one
// 2: two
// 3: three

Materializing Views

Convert views to containers:

auto view = vec 
    | views::filter([](int x) { return x % 2 == 0; })
    | views::transform([](int x) { return x * 2; });

// Convert to vector
std::vector<int> result(view.begin(), view.end());

// Or use ranges::to (C++23)
auto result2 = view | ranges::to<std::vector>();

Owning Views

views::all

Create view of existing range:

std::vector<int> vec = {1, 2, 3, 4, 5};
auto v = views::all(vec);

ranges::ref_view

Non-owning view (reference):

std::vector<int> vec = {1, 2, 3};
ranges::ref_view view(vec);

ranges::owning_view

Takes ownership of range:

auto owned = ranges::owning_view(std::vector{1, 2, 3});

Range Adaptors

Filtering and Transforming

std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

// Filter odd numbers, square them, take first 3
auto result = numbers
    | views::filter([](int x) { return x % 2 == 1; })
    | views::transform([](int x) { return x * x; })
    | views::take(3);

// Result: 1, 9, 25

Reverse and Drop

auto result = numbers
    | views::reverse
    | views::drop(2)
    | views::take(3);

// Start from end, skip 2, take 3: 8, 7, 6

Projection Support

Range algorithms accept projections:

struct Person {
    std::string name;
    int age;
};

std::vector<Person> people = {
    {"Alice", 30},
    {"Bob", 25},
    {"Charlie", 35}
};

// Find person with age > 30
auto it = rng::find_if(people, [](int age) { return age > 30; }, &Person::age);

// Sort by name
rng::sort(people, {}, &Person::name);

Practical Examples

Processing Text Lines

std::string text = "line1\\nline2\\nline3\\nline4";

for (auto line : text | views::split('\\n') | views::take(2)) {
    for (char c : line) std::cout << c;
    std::cout << '\\n';
}

Generating Fibonacci Sequence

auto fibonacci = views::iota(0)
    | views::transform([](int n) {
        // ... compute nth fibonacci
    })
    | views::take(10);
Conceptual Check

What is the key advantage of ranges views over eager evaluation?

Runtime Environment

Interactive Lab

1#include <iostream>
2#include <vector>
3#include <ranges>
4 
5namespace views = std::views;
6 
7int main() {
8 std::vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
9
10 // Compose operations
11 auto result = nums
12 | views::filter([](int x) { return x % 2 == 0; })
13 | views::transform([](int x) { return x * x; })
14 | views::take(3);
15
16 std::cout << "Even squares (first 3): ";
17 for (int x : result) {
18 std::cout << x << ' ';
19 }
20 std::cout << '\n';
21
22 // iota with reverse
23 std::cout << "Countdown: ";
24 for (int x : views::iota(1, 6) | views::reverse) {
25 std::cout << x << ' ';
26 }
27 std::cout << '\n';
28
29 return 0;
30}
System Console

Waiting for signal...