C++

Ranges & Views

10 question(s)

What are C++20 ranges?

Beginner
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.
std::vector<int> v{3,1,2};
std::ranges::sort(v);   // no begin()/end() needed
Real-world example std::ranges::sort(v) replaces std::sort(v.begin(), v.end()), removing a common iterator-mismatch bug.

Common follow-ups: How do range algorithms differ from classic ones? | What is a range?

STL Templates Ranges & Views

What is a view in the ranges library?

Beginner
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.

Common follow-ups: Why are views lazy? | Do views own their elements?

Ranges & Views Lambda Expressions Ranges & Views

What does the pipe operator | do with ranges?

Intermediate
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.

Common follow-ups: How is the pipeline evaluated? | Why is | readable?

Ranges & Views Lambda Expressions Ranges & Views

What are some commonly used range adaptors?

Intermediate
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.

Common follow-ups: What does views::transform do? | What does views::iota generate?

Ranges & Views STL Ranges & Views

How does lazy evaluation in views affect performance and behavior?

Advanced
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.

Common follow-ups: Why can re-iterating recompute? | How do you materialize a view?

Ranges & Views STL Ranges & Views

How do ranges make code safer than iterator pairs?

Beginner
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.

Common follow-ups: What iterator bug do ranges prevent? | What is a projection?

STL Ranges & Views Ranges & Views

What is a projection in range algorithms?

Advanced
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.

Common follow-ups: What does a projection transform? | How does it simplify sorting by a member?

Ranges & Views Lambda Expressions Ranges & Views

What is the difference between a range and a container?

Intermediate
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.

Common follow-ups: Do all ranges own their elements? | Is a container a range?

STL Ranges & Views Ranges & Views

What are the range concepts (input_range, forward_range, etc.)?

Advanced
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.

Common follow-ups: What does random_access_range guarantee? | Why constrain algorithms by range concepts?

Concepts & Constraints Ranges & Views Ranges & Views

Which header provides ranges and how do you use range algorithms?

Beginner
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.

Common follow-ups: Which headers are needed? | What namespace holds the algorithms?

STL Ranges & Views Ranges & Views