1. Problem It Solves
A lazy range eventually needs materialization into an owning container. std::ranges::to performs explicit conversion, while std::from_range selects a container constructor that consumes a range. 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 5: lazy view pipelines.
Day 39: range and iterator constness.
3. Core Idea
Views are a recipe; an owning container is the finished dish. ranges::to names the destination dish directly, and from_range labels which constructor interpretation is intended. 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 values = std::ranges::to<std::vector<int>>(view);
std::vector copy(std::from_range, values);5. How It Works
The sample builds the smallest expression or object needed for
std::ranges::toandstd::from_range.It materializes a transformed integer view and constructs a second vector from that range when supported. The compiler and library apply the relevant rule before the program observes the result.
The program prints or checks the owned transformed values or a precise availability message, making the important behavior easy to verify.
6. Common Mistakes
Materializing an infinite range never finishes; assuming conversion preserves references can be wrong because the destination normally owns new elements.
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 the boundary where a lazy pipeline must become owned, stored, sorted, or returned independently.
Avoid it when the task involves intermediate steps that can stay lazy and avoid allocation.
8. Simple Example
A filtered view of valid IDs is materialized into a vector before it is stored in a request object. The downloadable program keeps the data fixed so the output can be compared without entering input.
Complete sample code
Source file
cpp23/40_ranges_to_from_range/main.cpp
#include <iostream>
#include <ranges>
#include <vector>
#include <version>
int main() {
#if defined(__cpp_lib_ranges_to_container) && \
defined(__cpp_lib_containers_ranges)
auto squares = std::views::iota(1, 5)
| std::views::transform([](int n) { return n * n; });
auto values = std::ranges::to<std::vector<int>>(squares);
std::vector<int> copy(std::from_range, values);
for (int value : copy)
std::cout << value << ' ';
std::cout << '\n';
#else
std::cout << "ranges::to or from_range unavailable\n";
#endif
}
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 — When converting a view of references into
std::vector<T>, which values are copied or moved, and why does the vector not extend the source range's lifetime?