Ranges in C++ are a powerful and versatile feature introduced in the C++20 standard. They provide a unified interface for working with sequences of elements, such as arrays, containers, or generators. Ranges simplify and streamline common operations on sequences, enhancing code readability, maintainability, and performance.
Iterators and ranges are fundamental concepts in C++ that allow developers to work with sequences of elements efficiently.
Iterators are objects that provide a way to traverse the elements of a sequence sequentially. They serve as pointers to elements within a range and provide operations for navigating, dereferencing, and modifying elements.
Consider a simple example of iterating over a std::vector
using iterators:
#include
#include
int main() {
std::vector vec = {1, 2, 3, 4, 5};
// Using iterators to traverse the vector
for (auto it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << " ";
}
return 0;
}
// output //
1 2 3 4 5
std::vector<int>
with some values.vec.begin()
and vec.end()
to define the range over which we want to iterate.for
loop, incrementing the iterator (++it
) until it reaches the end of the vector (vec.end()
).*it
) to access the value at the current position.The range-based for loop, introduced in C++11, simplifies iteration over elements in a range by automatically handling iterators.
Using the same std::vector
example as before, we can rewrite the loop using the range-based for loop:
#include
#include
int main() {
std::vector vec = {1, 2, 3, 4, 5};
// Using range-based for loop
for (int num : vec) {
std::cout << num << " ";
}
return 0;
}
// output //
1 2 3 4 5
vec
, assigning each element to the variable num
in turn.The Standard Library Ranges introduced in C++20 provide a powerful and expressive way to work with sequences of elements.
The C++ Standard Library provides a rich set of algorithms for working with ranges, such as sorting, searching, and transforming elements.
Let’s consider an example where we sort a vector of integers using the std::sort
algorithm:
#include
#include
#include
int main() {
std::vector vec = {5, 2, 7, 1, 3};
// Sorting the vector
std::sort(vec.begin(), vec.end());
// Printing the sorted vector
for (int num : vec) {
std::cout << num << " ";
}
return 0;
}
// output //
1 2 3 5 7
<algorithm>
header to use the std::sort
algorithm.std::sort
with the range defined by vec.begin()
and vec.end()
to sort the elements in the vector.C++20 introduced views in the Standard Library, providing lazy evaluation and composability for ranges.
Let’s filter even numbers from a vector using the std::views::filter
view:
#include
#include
#include
int main() {
std::vector vec = {1, 2, 3, 4, 5};
// Filtering even numbers using views
auto even_view = vec | std::views::filter([](int x) { return x % 2 == 0; });
// Printing the filtered elements
for (int num : even_view) {
std::cout << num << " ";
}
return 0;
}
// output //
2 4
<ranges>
header to use views.std::views::filter
view to create a lazy view of the vector vec
, filtering out even numbers.Custom ranges and views allow developers to define their own transformations or filtering of elements in a range, tailored to their specific requirements.
Developers can define custom iterators to work with their own data structures or sequences. Custom iterators provide a way to traverse elements within a range and can be tailored to support specific operations or optimizations.
Let’s consider an example of a custom iterator for iterating over the elements of a custom data structure MyContainer
:
#include
class MyContainer {
private:
int data[5] = {1, 2, 3, 4, 5};
public:
class Iterator {
private:
int* ptr;
public:
Iterator(int* p) : ptr(p) {}
int& operator*() const {
return *ptr;
}
Iterator& operator++() {
++ptr;
return *this;
}
bool operator!=(const Iterator& other) const {
return ptr != other.ptr;
}
};
Iterator begin() { return Iterator(data); }
Iterator end() { return Iterator(data + 5); }
};
int main() {
MyContainer container;
// Using custom iterators to traverse the container
for (int num : container) {
std::cout << num << " ";
}
return 0;
}
// output //
1 2 3 4 5
MyContainer
that contains an array of integers.MyContainer
, we define a nested class Iterator
that represents an iterator for traversing the elements of the container.operator*
), incrementing (operator++
), and inequality comparison (operator!=
).begin()
and end()
member functions in MyContainer
that return iterators representing the beginning and end of the container, respectively.Custom views can be created to provide tailored transformations or filtering of elements in a range. Views are lightweight objects that represent a view into a range without owning the underlying elements.
Let’s create a custom view that filters even numbers from a range:
#include
#include
#include
class EvenView : public std::ranges::view_base {
private:
std::ranges::ref_view> vec;
public:
EvenView(std::vector& v) : vec(v) {}
class Iterator {
private:
decltype(vec.begin()) it;
public:
Iterator(decltype(it) iter) : it(iter) {}
int operator*() const {
return *it;
}
Iterator& operator++() {
++it;
while (it != vec.end() && *it % 2 != 0) {
++it;
}
return *this;
}
bool operator!=(const Iterator& other) const {
return it != other.it;
}
};
Iterator begin() { return Iterator(vec.begin()); }
Iterator end() { return Iterator(vec.end()); }
};
int main() {
std::vector vec = {1, 2, 3, 4, 5};
// Creating a custom view to filter even numbers
auto even_view = EvenView(vec);
// Using custom view in a range-based for loop
for (int num : even_view) {
std::cout << num << " ";
}
return 0;
}
// output //
2 4
EvenView
that filters even numbers from a range.EvenView
inherits from std::ranges::view_base
to indicate that it is a view.EvenView
, we define a nested class Iterator
that represents an iterator for traversing the elements of the view.operator*
), incrementing (operator++
), and inequality comparison (operator!=
).begin()
and end()
member functions in EvenView
that return iterators representing the beginning and end of the view, respectively.EvenView
in a range-based for loop to iterate over the filtered even numbers.Ranges in C++ provide a powerful and flexible way to work with sequences of elements. By leveraging iterators, standard library algorithms, and views, developers can write concise and expressive code for manipulating ranges efficiently. Embracing ranges can lead to clearer, more maintainable code with improved performance and readability.Happy coding !❤️