Home Item 24 - Range (English)
Post
Cancel

Item 24 - Range (English)

Ranges in C++20

C# has Linq, and Python has built-in functions like filter and map for quickly filtering elements that meet specific conditions from a collection. What about C++? Do you have to loop through a container and slowly find the parts you need? No! C++20 introduces the <ranges> header, which provides many convenient functions to handle such scenarios.

code

The following example demonstrates how to find even numbers from a set, square them, and then reverse the order of the output.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <vector>
#include <ranges>
#include <algorithm>
int main()
{
    std::vector<int> numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

    // Filter, transform, and sort the numbers
    auto result = numbers
        | std::views::filter([](int n) { return n % 2 == 0; }) // Filter even numbers
        | std::views::transform([](int n) { return n * n; })  // Square the filtered even numbers
        | std::views::reverse; // Reverse the order

    // Output the result
    for (int n : result) {
        std::cout << n << " ";
    }
    std::cout << std::endl;

    return 0;
}

Execution result

Desktop View

Available Range Adaptors

C++20’s <ranges> library introduces several range adaptors used to modify or operate on range data. Common adaptors include:

  1. std::views::filter: Filters elements that meet a condition.
  2. std::views::transform: Applies a transformation to each element.
  3. std::views::reverse: Reverses the order of the range.
  4. std::views::take: Takes the first n elements of the range.
  5. std::views::drop: Drops the first n elements of the range.
  6. std::views::take_while: Takes continuous elements that meet a condition until it fails.
  7. std::views::drop_while: Drops continuous elements that meet a condition until it fails.
  8. std::views::concat: Concatenates multiple ranges.
  9. std::views::zip: Combines elements from multiple ranges into tuples.
  10. std::views::join: Flattens ranges of ranges into a single range.
  11. std::views::elements: Extracts a member or index from elements of the range.

Another Example with Structs

Here is an example with an NBAPlayer struct:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <iostream>
#include <vector>
#include <ranges>
#include <algorithm>

 // Define NBAPlayer structure
 struct NBAPlayer {
     std::string name;
     int number;

     // Friend function for << operator
     friend std::ostream& operator<<(std::ostream& os, const NBAPlayer& player)
     {
         os << "Name: " << player.name << ", Number: " << player.number;
         return os;
     }
 };

int main()
{
    // Create a container of NBAPlayers
    std::vector<NBAPlayer> players = { {"Kobe Bryant",24},{"MJ",23},{"LBJ", 23}, {"AI",3},{"AD", 0} };

    for  (auto p : players)
    {
        std::cout << p << std::endl;
    }

    // Use range adaptors to filter and transform players
    auto filtered_players = players
        | std::views::take(5)
        | std::views::transform([](NBAPlayer player) {player.number = 24; return  player; });

    std::cout << std::endl;
    std::cout << "---filtered_players---:" << std::endl;

    for (auto p : filtered_players)
    {
        std::cout << p << std::endl;
    }

    std::cout << std::endl;
    std::cout << "---players---:" << std::endl;

    for (auto p : players)
    {
        std::cout << p << std::endl;
    }

    return 0;
}

Execution result:

Desktop View

Note: Using range adaptors and transformation operations, such as std::views::transform, does not change the values in the original container.

These adaptors and operations create new “views” or “ranges” over the original data without modifying it.

Usage Scenarios

  1. Data Filtering: When you need to filter elements that meet specific conditions from a collection, use std::views::filter.
  2. Data Transformation: When you need to apply a transformation to each element in a collection (e.g., square, negate, format), use std::views::transform.
  3. Combining Operations: You can chain multiple range adaptors to create complex data processing pipelines without creating temporary containers or explicit iterations.

This is a very convenient feature—give it a try next time!

☝ツ☝

This post is licensed under CC BY 4.0 by the author.

👈 ツ 👍