C++ Ranges
Modern functional programming with containers and algorithms
🔄 What are C++ Ranges?
C++ Ranges provide a modern way to work with sequences of data using functional programming style. They make algorithms more readable, composable, and efficient by chaining operations together seamlessly.
// Modern ranges approach
#include <ranges>
#include <vector>
#include <iostream>
std::vector<int> numbers = {1, 2, 3, 4, 5, 6};
auto result = numbers
| std::views::filter([](int n) { return n % 2 == 0; })
| std::views::transform([](int n) { return n * n; });
// result contains: 4, 16, 36
Output:
Filtered and squared even numbers: 4, 16, 36
Key Ranges Features
Composable
Chain operations with pipe operator
data | filter | transform | take
Lazy Evaluation
Process data only when needed
// No work done until iteration
auto view = data | filter | take(5);
Readable
Express intent clearly
// Clear: "filter then transform"
data | views::filter(pred)
| views::transform(func)
Efficient
No unnecessary copies or allocations
// Views don't copy data
auto view = container | views::reverse;
🔹 Basic Range Operations
Basic range operations in C++20 Ranges allow filtering, querying, and inspecting sequences without modifying them directly. For example, checking how many numbers in a range exceed a value (like 30) is a common filtering task. Similarly, verifying whether all elements satisfy a condition—such as being positive—is a useful query. These operations form the foundation of the Ranges library, providing a more expressive and composable alternative to traditional loops and algorithms. Ranges work seamlessly with standard containers and can dramatically improve code clarity and maintainability in data processing tasks.
#include <ranges>
#include <vector>
#include <iostream>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// Filter even numbers
auto evens = numbers | std::views::filter([](int n) {
return n % 2 == 0;
});
// Transform to squares
auto squares = numbers | std::views::transform([](int n) {
return n * n;
});
// Take first 3 elements
auto first_three = numbers | std::views::take(3);
// Print results
for (int n : evens) std::cout << n << " ";
std::cout << std::endl;
return 0;
}
Output:
Even numbers: 2 4 6 8 10
Squares: 1 4 9 16 25 36 49 64 81 100
First three: 1 2 3
🔹 Chaining Operations
Chaining range operations creates readable pipelines that process data sequentially without intermediate
storage. For instance, data | filter(pred) | transform(func) | take(3) filters,
transforms, and limits results in one expression. This functional approach enhances clarity and maintainability by
separating concerns into distinct steps. It also leverages compiler optimizations for efficient execution. Chaining
is central to modern C++ ranges, enabling complex data transformations with minimal code.
#include <ranges>
#include <vector>
#include <iostream>
#include <string>
int main() {
std::vector<std::string> words = {
"hello", "world", "cpp", "ranges", "are", "awesome"
};
// Chain: filter long words, transform to uppercase, take 2
auto result = words
| std::views::filter([](const std::string& s) {
return s.length() > 3;
})
| std::views::transform([](const std::string& s) {
std::string upper = s;
std::transform(upper.begin(), upper.end(), upper.begin(), ::toupper);
return upper;
})
| std::views::take(2);
for (const auto& word : result) {
std::cout << word << " ";
}
std::cout << std::endl;
return 0;
}
Output:
HELLO WORLD
🔹 Range Algorithms
Range-based algorithms like std::ranges::find and std::ranges::count_if operate
directly on ranges without begin/end iterators. They support projections and predicates, making common
operations more concise. For example, std::ranges::find(numbers, 5) locates a value. These algorithms
are composable with views and work with any range type, improving code expressiveness and reducing iterator
boilerplate. They are a key part of the C++20 ranges library for modern, clean algorithmic code.
#include <ranges>
#include <algorithm>
#include <vector>
#include <iostream>
int main() {
std::vector<int> numbers = {3, 1, 4, 1, 5, 9, 2, 6};
// Sort using ranges
std::ranges::sort(numbers);
// Find element
auto it = std::ranges::find(numbers, 5);
if (it != numbers.end()) {
std::cout << "Found 5 at position: "
<< std::distance(numbers.begin(), it) << std::endl;
}
// Count elements greater than 3
auto count = std::ranges::count_if(numbers, [](int n) {
return n > 3;
});
std::cout << "Numbers > 3: " << count << std::endl;
return 0;
}
Output:
Found 5 at position: 4
Numbers > 3: 4