std::vector<int> v{3,1,2};
std::ranges::sort(v); // no begin()/end() needed
C++
Topics in this category
Casting & Conversions
Compilation & Linking
Concepts & Constraints
Concurrency
Const Correctness
Constexpr & Compile-Time
Coroutines
Enums & Enum Classes
Exception Handling
I/O Streams
Lambda Expressions
Memory Management
Modern C++
Move Semantics
Namespaces
OOP & Classes
Operator Overloading
Preprocessor & Macros
RAII & Smart Pointers
Ranges & Views
C++
Ranges & Views
Ranges (C++20) are an abstraction over 'something you can iterate'—anything with a begin and end—paired with algorithms that operate on whole ranges instead of iterator pairs. The std::ranges algorithms (std::ranges::sort(v)) are cleaner and safer than passing v.begin(), v.end(), and they enable composable views.
Real-world example
std::ranges::sort(v) replaces std::sort(v.begin(), v.end()), removing a common iterator-mismatch bug.
STL
Templates
Ranges & Views
A view is a lightweight, non-owning, lazily-evaluated range that adapts another range without copying its elements. Views (like std::views::filter, std::views::transform) compute elements on demand as you iterate, so composing them doesn't create intermediate containers. They're cheap to copy and compose.
auto evens = v | std::views::filter([](int x){ return x%2==0; });
Real-world example
A pipeline of views filters and transforms a vector lazily without allocating intermediate vectors.
Ranges & Views
Lambda Expressions
Ranges & Views
The | operator composes range adaptors into a pipeline, feeding the output of one view into the next, read left to right. It makes transformations readable, like Unix pipes: range | filter | transform | take. Each stage is a lazy view, so the whole pipeline evaluates on demand during iteration.
auto r = v | std::views::filter(isEven)
| std::views::transform(square);
Real-world example
A data pipeline reads as filter-then-transform via |, far clearer than nested algorithm calls.
Ranges & Views
Lambda Expressions
Ranges & Views
Common adaptors include views::filter (keep elements matching a predicate), views::transform (apply a function), views::take/drop (first/skip n), views::reverse, views::join (flatten), views::split, views::iota (generate a sequence), and views::keys/values (for maps). They compose to express complex transformations declaratively.
auto first3 = std::views::iota(1) | std::views::take(3); // 1,2,3
Real-world example
views::iota with take generates a bounded sequence lazily without building a container.
Ranges & Views
STL
Ranges & Views
Views compute elements only when iterated and don't store results, so composing many views avoids intermediate allocations and can short-circuit (e.g., with take). But re-iterating recomputes, side-effecting transforms run per traversal, and some views are single-pass. Understanding laziness is key to correct and efficient range pipelines; materialize with std::ranges::to (C++23) when you need a container.
auto v2 = pipeline | std::ranges::to<std::vector>(); // materialize (C++23)
Real-world example
A lazy pipeline avoids building temporaries, but the team materializes the result once for repeated use.
Ranges & Views
STL
Ranges & Views
Passing begin()/end() from different containers, or in the wrong order, is a classic bug that ranges eliminate by taking a single range object. Range algorithms also support projections and are constrained by concepts, giving clearer error messages. The whole-range interface is harder to misuse.
std::ranges::find(v, target); // can't mismatch begin/end
Real-world example
A subtle bug from mixing two containers' iterators is impossible once code uses range algorithms.
STL
Ranges & Views
Ranges & Views
A projection is an optional callable passed to a range algorithm that transforms each element before the algorithm's core logic (comparison, predicate) sees it. It lets you, for example, sort a vector of structs by a member without writing a custom comparator, by projecting to that member.
std::ranges::sort(people, {}, &Person::age); // sort by age
Real-world example
Sorting a vector of Person by age uses a projection (&Person::age) instead of a comparator lambda.
Ranges & Views
Lambda Expressions
Ranges & Views
A container owns its elements and manages their storage (vector, map). A range is any type providing begin()/end()—which includes containers, views, arrays, and generated sequences. Views are ranges that don't own data. So all containers are ranges, but not all ranges are containers.
Real-world example
A function templated on a range accepts vectors, arrays, and lazy views uniformly.
STL
Ranges & Views
Ranges & Views
Ranges are categorized by iterator capability via concepts: input_range (single-pass read), forward_range (multi-pass), bidirectional_range, random_access_range, and contiguous_range (elements in contiguous memory). Algorithms and views are constrained by these concepts, so the compiler enforces that a range supports the operations an algorithm needs.
Real-world example
A random-access-only algorithm won't compile for a single-pass input range, caught at compile time by concepts.
Concepts & Constraints
Ranges & Views
Ranges & Views
Ranges live in <ranges> (views/adaptors) and <algorithm> (the std::ranges:: algorithm overloads). You call algorithms in the std::ranges namespace passing a whole range, and build view pipelines with std::views::. Support requires a C++20-capable compiler and standard library.
#include <ranges>
#include <algorithm>
std::ranges::for_each(v, [](int x){ /*...*/ });
Real-world example
Adopting <ranges> lets the team write std::ranges algorithms and view pipelines across the codebase.
STL
Ranges & Views
Ranges & Views