C++23 ranges provide a powerful way to work with sequences of data using functional programming principles, making code more expressive and easier to read.
#include <iostream>
#include <ranges>
#include <vector>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
auto squares = numbers | std::views::transform([](int n) { return n * n; });
for (int n : squares) {
std::cout << n << " ";
}
return 0;
}
Introduction to C++23 Ranges
What are Ranges?
C++23 ranges are a powerful new feature that streamlines the way we work with collections of data in C++. Ranges provide a higher-level abstraction for manipulating sequences, making our code more intuitive and expressive compared to traditional iterator-based approaches.
Why Use Ranges in C++23?
By using ranges, developers can write code that is not only more readable but also more concise. This leads to clearer intent and reduced boilerplate. For example, using ranges allows you to perform complex transformations and queries with minimal syntax. In real-world applications, ranges can enhance performance and readability, enabling developers to focus more on the algorithm rather than the implementation details.

Core Concepts of Ranges
Understanding the Ranges Namespace
The Ranges namespace is a fundamental part of the C++ standard library, introduced in C++20 and enhanced in C++23. It encompasses various components that allow for distinct operations on collections. Key components of the `std::ranges` namespace include:
- Views: Non-owning references to sequences, allowing for efficient manipulation without unnecessary copies.
- Adaptors: Functions that modify ranges to add functionality, such as filtering or transforming.
Basic Range Definition
A range in C++23 is defined as a sequence of elements that can be iterated through. This can be done using range views and adaptors which act as building blocks for creating custom range behaviors.
Range Views
Range views serve as a crucial component of ranges in C++, providing a lazy evaluation mechanism. This means that operations defined by views do not execute until they are actually needed, which saves memory and processing time. Common examples of range views include `std::views::filter` and `std::views::transform`, which allow you to view data differently without modifying the underlying data structure directly.
Range Adaptors
Range adaptors are applied to existing ranges to introduce new behavior. They allow for transformation, filtering, or other manipulations seamlessly. Some of the most common range adaptors are:
- `views::filter`: To filter elements based on a specific condition.
- `views::transform`: To apply a transformation to every element in a range.

Creating Ranges with Standard Library Features
Working with Containers and Ranges
C++23 ranges can be directly integrated with standard containers such as `std::vector` and `std::list`. This makes it easy to manipulate data without having to write complex loops. Here’s a simple example of iterating through a vector with ranges:
#include <vector>
#include <ranges>
#include <iostream>
std::vector<int> nums = {1, 2, 3, 4, 5};
for (int n : nums | std::views::transform([](int i) { return i * i; })) {
std::cout << n << ' '; // Outputs: 1 4 9 16 25
}
In this code, we create a view that transforms each number in `nums` while iterating, demonstrating both the power and simplicity of ranges.
Ranges with Algorithms
Combining ranges with standard library algorithms is where the power of C++23 truly shines. Operations become more intuitive and expressive. For example, using `std::ranges::sort` and `std::ranges::find` can be done elegantly:
#include <algorithm>
#include <ranges>
#include <vector>
#include <iostream>
std::vector<int> nums = {4, 1, 3, 5, 2};
std::ranges::sort(nums);
bool found = std::ranges::find(nums, 3) != nums.end(); // True
In this snippet, we sort the vector and then check if the value `3` exists, all with streamlined syntax.

Advanced Range Techniques
Custom Range Types
One of the powerful features of C++23 ranges is the ability to create custom range types. This allows you to define new sequences that can be used just like established ranges. For example, you could implement a custom generator that yields a sequence of numbers, following the pattern you choose.
Utilizing Composable Ranges
Ranges can be chained together, allowing for composable ranges. This means you can take the output of one range adaptor and feed it directly into another. This capability highlights the flexibility of ranges and can lead to concise and expressive code. An example of this would be filtering even numbers and then transforming those to their square:
#include <vector>
#include <ranges>
#include <iostream>
std::vector<int> nums = {1, 2, 3, 4, 5, 6};
auto even_squared = nums | std::views::filter([](int n) { return n % 2 == 0; })
| std::views::transform([](int n) { return n * n; });
for (int n : even_squared) {
std::cout << n << ' '; // Outputs: 4 16 36
}
This example emphasizes the beauty of using ranges to create sophisticated data manipulation pipelines.
Lazy Evaluation with Ranges
A hallmark of range views is lazy evaluation. Lazy evaluation leads to increased efficiency by postponing computations until absolutely necessary. Here’s a simple demonstration to highlight its benefits in terms of performance:
#include <vector>
#include <ranges>
#include <iostream>
std::vector<int> large_nums(1000000);
std::iota(large_nums.begin(), large_nums.end(), 1);
auto lazy_range = large_nums | std::views::filter([](int n) { return n % 2 == 0; });
std::cout << "First element of lazy range: " << *lazy_range.begin() << '\n';
In this snippet, the filtering won’t occur until we access the first element, showcasing the efficiency that comes with lazy evaluation.

Practical Applications of C++23 Ranges
Filtering and Transforming Data
Ranges are particularly effective in scenarios that require filtering and transforming data. For instance, you can easily draft code to filter out even numbers from a dataset:
#include <vector>
#include <ranges>
#include <iostream>
std::vector<int> nums = {1, 2, 3, 4, 5, 6};
auto even_numbers = nums | std::views::filter([](int n) { return n % 2 == 0; });
for (int n : even_numbers) {
std::cout << n << ' '; // Outputs: 2 4 6
}
In this example, constructing a view of even numbers is both simple and efficient. The range adaptors allow you to specify the condition directly, making your code easier to understand.
Performance Considerations
When using C++23 ranges, it’s essential to evaluate the performance implications. Ranges enable a more expressive style of coding, but it’s still crucial to understand the trade-offs involved. Ranges that use lazy evaluation can save time and processing power by eliminating unnecessary calculations.

Future of Ranges in C++
Advancements Beyond C++23
As we look toward the future, the capabilities of ranges are only expected to grow. Community feedback plays a vital role in shaping advancements, with developers encouraging enhancements to improve usability and functionality. This ensures that C++23 ranges stay relevant and beneficial for developers.

Conclusion
C++23 ranges represent a significant leap forward in the way we write C++ code. By abstracting away much of the complexity involved with iterators and loops, ranges offer developers the ability to express their intentions more clearly and concisely. Whether you’re filtering data, transforming collections, or composing complex algorithms, C++23 ranges provide the tools necessary for efficient, modern programming.

Additional Resources
For those looking to dive deeper into the topic, several official documentation sites, tutorials, and example projects are available online. Engage with the community and share your own use cases or examples on forums like Stack Overflow or GitHub to continue the learning process.