1. Problem It Solves
Ranges describe sequences directly, and views build lazy transformations without eagerly creating intermediate containers. Iterators mark positions; a sentinel marks where traversal stops. The goal is to make the relevant rule visible in code instead of relying on an assumption about what “C++23 support” means.
2. Prerequisites
Day 4: generic functions and concepts.
Loops and standard containers from earlier C++ study.
3. Core Idea
Imagine a conveyor belt. A view adds lightweight stations such as filter and transform; values are processed only when an iterator pulls them toward the sentinel. Read the syntax from left to right, identify the value, object, or range being transformed, and then check its resulting type, lifetime, or ownership. Standardization and implementation are separate: a C++23 mode may still lack one library component, so a feature-test macro is part of responsible portable use.
4. Minimal Syntax
auto v = std::views::iota(1, 8) | std::views::filter(pred);5. How It Works
The sample builds the smallest expression or object needed for Ranges, Views, Iterators, and Sentinels.
It creates a lazy integer view, then walks from its iterator to its sentinel. The compiler and library apply the relevant rule before the program observes the result.
The program prints or checks only values that satisfy the filter, transformed at the moment they are read, making the important behavior easy to verify.
6. Common Mistakes
Returning a view that refers to a destroyed local range creates dangling access; assuming iterator and sentinel have the same type also breaks generic code.
Enabling C++23 mode without checking the relevant feature macro may select code that the installed compiler or standard library does not implement yet.
7. When to Use It
Use it when the task involves composable read pipelines where lazy evaluation avoids temporary containers.
Avoid it when the task involves a plain loop that is already clearer or a pipeline whose source will not outlive the view.
8. Simple Example
A report filters positive measurements and doubles them only while producing display rows. The downloadable program keeps the data fixed so the output can be compared without entering input.
Complete sample code
Source file
cpp23/05_ranges_views_iterators_sentinels/main.cpp
#include <iostream>
#include <ranges>
int main() {
auto values = std::views::iota(1, 8)
| std::views::filter([](int n) { return n % 2 == 0; })
| std::views::transform([](int n) { return n * 2; });
auto first = values.begin();
auto last = values.end(); // The sentinel may have another type.
while (first != last) {
std::cout << *first << ' ';
++first;
}
std::cout << '\n';
}
9. Key Takeaways
Separate the language rule from compiler and library availability.
Keep lifetime, ownership, and deduced types visible when they affect correctness.
Prefer the smallest syntax that communicates the intent.
Guard facilities that are not yet uniformly implemented.
10. Self-Check Questions
Easy — What is the smallest syntax in Section 4, and what main job does it perform?
Medium — Read the sample program: which value, type, or branch is observed, and why?
Hard — Why can
auto last = range.end()be a sentinel type different fromdecltype(range.begin()), and which comparison must still work?