Ranges in C++

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.

Basic Concepts

  • Range: A sequence of elements, typically represented by iterators or views.
  • Iterators: Objects that provide a way to traverse the elements of a range sequentially.
  • Views: Lightweight objects that represent a view into a range without owning the underlying elements.

Iterators and Ranges

Iterators and ranges are fundamental concepts in C++ that allow developers to work with sequences of elements efficiently.

Iterators

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.

Example:

Consider a simple example of iterating over a std::vector using iterators:

				
					#include <iostream>
#include <vector>

int main() {
    std::vector<int> 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

				
			

Explanation:

  • We initialize a std::vector<int> with some values.
  • We use iterators vec.begin() and vec.end() to define the range over which we want to iterate.
  • We traverse the vector using a for loop, incrementing the iterator (++it) until it reaches the end of the vector (vec.end()).
  • Inside the loop, we dereference the iterator (*it) to access the value at the current position.

Range-Based For Loop

The range-based for loop, introduced in C++11, simplifies iteration over elements in a range by automatically handling iterators.

Example:

Using the same std::vector example as before, we can rewrite the loop using the range-based for loop:

				
					#include <iostream>
#include <vector>

int main() {
    std::vector<int> 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

				
			

Explanation:

  • The range-based for loop iterates over each element in the vector vec, assigning each element to the variable num in turn.
  • The loop automatically handles the iterators behind the scenes, making the code cleaner and more concise.

Standard Library Ranges

The Standard Library Ranges introduced in C++20 provide a powerful and expressive way to work with sequences of elements.

Standard Library Algorithms

The C++ Standard Library provides a rich set of algorithms for working with ranges, such as sorting, searching, and transforming elements.

Example:

Let’s consider an example where we sort a vector of integers using the std::sort algorithm:

				
					#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    std::vector<int> 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

				
			

Explanation:

  • We include the <algorithm> header to use the std::sort algorithm.
  • We call std::sort with the range defined by vec.begin() and vec.end() to sort the elements in the vector.
  • Finally, we print the sorted vector using a range-based for loop.

Standard Library Views

C++20 introduced views in the Standard Library, providing lazy evaluation and composability for ranges.

Example:

Let’s filter even numbers from a vector using the std::views::filter view:

				
					#include <iostream>
#include <vector>
#include <ranges>

int main() {
    std::vector<int> 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

				
			

Explanation:

  • We include the <ranges> header to use views.
  • We use the std::views::filter view to create a lazy view of the vector vec, filtering out even numbers.
  • We iterate over the filtered view using a range-based for loop and print the filtered elements.

Custom Ranges and Views

Custom ranges and views allow developers to define their own transformations or filtering of elements in a range, tailored to their specific requirements.

Writing Custom Iterators

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.

Example:

Let’s consider an example of a custom iterator for iterating over the elements of a custom data structure MyContainer:

				
					#include <iostream>

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

				
			

Explanation:

  • We define a custom data structure MyContainer that contains an array of integers.
  • Inside MyContainer, we define a nested class Iterator that represents an iterator for traversing the elements of the container.
  • We implement the necessary iterator operations such as dereferencing (operator*), incrementing (operator++), and inequality comparison (operator!=).
  • We provide begin() and end() member functions in MyContainer that return iterators representing the beginning and end of the container, respectively.
  • Finally, we use custom iterators in a range-based for loop to traverse the elements of the container.

Writing Custom Views

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.

Example:

Let’s create a custom view that filters even numbers from a range:

				
					#include <iostream>
#include <ranges>
#include <vector>

class EvenView : public std::ranges::view_base {
private:
    std::ranges::ref_view<std::vector<int>> vec;
    
public:
    EvenView(std::vector<int>& 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<int> 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

				
			

Explanation:

  • We define a custom view EvenView that filters even numbers from a range.
  • EvenView inherits from std::ranges::view_base to indicate that it is a view.
  • Inside EvenView, we define a nested class Iterator that represents an iterator for traversing the elements of the view.
  • We implement the necessary iterator operations such as dereferencing (operator*), incrementing (operator++), and inequality comparison (operator!=).
  • We provide begin() and end() member functions in EvenView that return iterators representing the beginning and end of the view, respectively.
  • Finally, we use the custom view 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 !❤️

Table of Contents

Contact here

Copyright © 2025 Diginode

Made with ❤️ in India