C++20 Ranges, Views, and the Spaceship Operator
Standard Library Containers and Iterators

6.6 C++20 Ranges, Views, and the Spaceship Operator

Standard algorithms require passing verbose .begin() and .end() iterators, which is repetitive and error prone. C++20 introduced the <ranges> library, allowing us to pass containers directly. It also added views for lazy, zero copy pipeline evaluations, and the spaceship operator <=> for automatic comparison generation.

Let's look at range pipelines, lazy evaluation views, and three way comparison mechanics.

Ranges: Passing Containers Directly

In C++20, you no longer need to write std::sort(vec.begin(), vec.end());. You can pass the vector container directly to the ranges library equivalent, reducing boilerplate:

#include <iostream>
#include <vector>
#include <algorithm>
#include <ranges>

int main() {
    std::vector<int> vec{40, 10, 30, 20};
    std::ranges::sort(vec); // Clean syntax: passes container directly!
    return 0;
}
Sorting a vector directly using C++20 ranges.

Lazy Range Views, Pipe Composition, and Compile-Time Composability

Range Views are lazy, non owning wrappers. In other languages, if you filter an array and then map it, the language allocates a temporary array on the heap for the filtered results, then loops over it again to map it. This destroys performance.

C++ Views compose at compile time. When you chain views using the pipe | operator, the compiler fuses the logic into a single, highly optimized state machine. No intermediate heap arrays are allocated, and the data is only processed exactly when you request it.

#include <iostream>
#include <vector>
#include <ranges>

int main() {
    std::vector<int> numbers{1, 2, 3, 4, 5, 6};

    // Pipeline: Filter even numbers and double them
    auto result = numbers 
        | std::views::filter([](int n) { return n % 2 == 0; })
        | std::views::transform([](int n) { return n * 2; });

    // Calculation runs on the fly only during loop execution!
    for (int n : result) {
        std::cout << n << ' '; // Prints: 4 8 12
    }
    return 0;
}
Lazy filtering and transforming container ranges.

The Spaceship Operator (<=>)

Writing comparison operators (<, <=, >, >=, ==, !=) for custom structs requires writing six separate functions. C++20 solved this by introducing the Spaceship Operator (or three way comparison operator: <=>).

If you declare the spaceship operator set to default, the compiler automatically generates all six comparison operators for you based on member declaration order:

#include <iostream>
#include <compare> // Required for spaceship operator

struct Point {
    int x;
    int y;
    auto operator<=>(const Point&) const = default; // Generates all 6 comparisons!
};

int main() {
    Point p1{1, 2};
    Point p2{1, 3};
    std::cout << std::boolalpha << (p1 < p2) << '\n'; // Prints: true (compares y since x is equal)
    return 0;
}
Automatic comparison generation using the spaceship operator.

Performance Benefits

Under the hood, range views avoid intermediate heap allocated vectors. By combining multiple filters and transforms into a single iterator sweep, the compiler generates assembly that performs calculations in place. This saves memory bandwidth and keeps data inside CPU L1 cache lines.

* The Tap Water Metaphor: Classic STL algorithms are like filtering all the water in a lake and bottling it (creating intermediate copies). C++20 range views are like connecting a filter attachment directly to the tap. The water is only filtered as it runs out when you turn it on (lazy evaluation), saving storage and work.

Finished reading this lesson?