You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
At a high level, a range is something you can iterate over. A range is a rich abstraction over iterators that simplifies and amplifies your ability to use the Standard Template Library (STL).
12
12
13
-
STL algorithms usually take iterators that point to the collection they operate on. But generally speaking, you want to operate on the entire collection. Consider how you sort a `vector` today. To call `std::sort()`, you pass two iterators marking the beginning and end of the `vector`. That's flexible, but is noisy since much of the time you just want to sort the entire `vector`, but you still have to pass the beginning and end of what to sort.
13
+
STL algorithms usually take iterators that point to the collection they operate on. But you usually want to operate on the entire collection. Consider how you sort a `vector` today. To call `std::sort()`, you pass two iterators marking the beginning and end of the `vector`. That's flexible, but is noisy since much of the time you just want to sort the entire `vector`, yet you still have to pass the beginning and end of what to sort.
14
14
15
-
With ranges, you can call `std::ranges::sort(myVector);`. It is treated as if you had called `std::sort(myVector.begin(), myVector.end());` In range libraries, in addition to taking iterators, STL algorithms can take ranges as parameters. Examples of range algorithms available in `<algorithm>` include `copy`, `copy_n`, `copy_if`, `all_of`, `any_of`, and `none_of`, `find`, `find_if`, and `find_if_not`, `count` and `count_if`, `for_each` and `for_each_n`, `equal` and `mismatch`.
15
+
With ranges, you can call `std::ranges::sort(myVector);` It is treated as if you had called `std::sort(myVector.begin(), myVector.end());` In range libraries, in addition to taking iterators, algorithms can take ranges as parameters. Examples of range algorithms available in `<algorithm>` include `copy`, `copy_n`, `copy_if`, `all_of`, `any_of`, and `none_of`, `find`, `find_if`, and `find_if_not`, `count` and `count_if`, `for_each` and `for_each_n`, `equal` and `mismatch`.
16
16
17
-
Simplified code that is more readable is great, but the benefits of ranges extend further to making it much easier to filter and transform collections of data, and to compose STL algorithms more easily.
17
+
More readable and easier to write code is great, but the benefits of ranges goes further than that. They also make it much easier to filter and transform collections of data, and to compose STL algorithms more easily.
18
18
19
19
Because ranges don't own elements like a container does, they are lightweight.
auto output = input | std::views::filter([](const int n) {return n % 3 == 0; }) | std::views::transform([](const int n) {return n * n; });
36
36
```
37
-
> [!NOTE] The ranges examples need to be compiled with the [`/std:c++latest`](../build/reference/std-specify-language-standard-version.md) compiler option.
38
37
39
-
In addition to being a little easier to read, it avoid the memory allocation required for `intermediate` in the non-ranges example.
38
+
In addition to being easier to read, it avoids the memory allocation required for `intermediate` in the non-ranges example.
40
39
41
-
In the code above, the input is first filtered to just the entries that are divisible by three. The result is combined with a transformation that squares those elements (the ones divisible by three). The '`|`' symbol chains the range view operations together, and is read left to right.
40
+
In the code above, each element that is divisible by threeis combined with a transformation to square that element. The '`|`' symbol chains the operations together, and is read left to right.
42
41
43
42
The result, `output`, is itself a type of range called a view, which is discussed next.
44
43
44
+
> [!NOTE] The ranges examples require the [`/std:c++latest`](../build/reference/std-specify-language-standard-version.md) compiler option.
45
+
46
+
45
47
## Views
46
48
47
49
A view is essentially a range that takes another range and transforms it in the way that you specify.
48
50
49
-
A view is lightweight. Like a range, a view doesn't own the elements it provides access to. The time it takes to copy, move, or assign a view is constant, regardless of how many elements it points to.
51
+
A view is lightweight. Like a range, it doesn't own the elements it provides access to. The time it takes to copy, move, or assign a view is constant, regardless of how many elements it points to.
50
52
51
-
Views are composable. In the example above, the view of vector elements that are divisible by three is then combined with the view that squares those elements.
53
+
Views are composable. In the example above, the view of vector elements that are divisible by three is combined with the view that squares those elements.
52
54
53
-
The elements of a view are evaluated lazily. That is, the transformation you apply to yield elements in a view is not evaluated until you ask for an element. For example, if you run the following code in a debugger, and put a breakpoint on the lines `auto divisible_by_three = ...` and `auto square = ...`, you'll see that you hit the first breakpoint as every element is tested for divisibility by three, and then the second breakpoint as the ones that are divisible by three are squared.
55
+
The elements of a view are evaluated lazily. That is, the transformations you apply to yield the elements in a view are not evaluated until you ask for an element in the view. For example, if you run the following code in a debugger, and put a breakpoint on the lines `auto divisible_by_three = ...` and `auto square = ...`, you'll see that you hit the `divisible_by_three` lambda breakpoint as each element in `input`is tested for divisibility by three. Then you'll see the `square` lambda hit as those elements that are divisible by three are squared.
54
56
55
57
```cpp
56
58
// requires /std:c++latest
@@ -77,36 +79,32 @@ int main()
77
79
78
80
## View adaptors
79
81
80
-
A view adaptor provides a view over a range. The range being viewed remains unchanged. The produced view doesn't own any elements. Instead, a view allows you to iterate over the underlying range, but using customized behavior that you specify.
82
+
A view adaptor provides a view over a range. The range being viewed is not changed. A view doesn't own any elements. Instead, a view allows you to iterate over the underlying range, but using customized behavior that you specify.
81
83
82
84
In the example above, the first view acts like an iterator that only provides the elements of `input` that are divisible by three. The other view acts like an iterator that takes the elements divisible by three, and provides their square.
83
85
84
-
The `<ranges>` library provides many kinds of view adaptors. In addition to the filter and transform views above, there are views that take or skip the first N elements of another view, reverse the order of a view, join views, skip elements of another view until a condition is met, transform the elements of another view, among others.
86
+
The `<ranges>` library provides many kinds of view adaptors. In addition to the filter and transform views above, there are views that take or skip the first N elements of another view, reverse the order of a view, join views, skip elements of another view until a condition is met, transform the elements of another view, and more.
85
87
86
88
## Range adaptors
87
89
88
90
A range adaptor produces a new range from an existing range. Views, discussed above, are a kind of range adaptor.
89
91
90
-
Range adaptors produce lazily evaluated ranges. That is, you don't incur the cost of transforming every element in the range; only the ones that you access.
92
+
Range adaptors produce lazily evaluated ranges. That is, you don't incur the cost of transforming every element in the range--only the ones that you access.
91
93
92
94
Range adaptors come in many forms. For example, there are range adaptors that allow you to filter another range based on a predicate (`view::filter()`), transform the elements in a range (`view::transform`), split a range (`view::split()`), and more.
93
95
94
-
Range adaptors can be chained, as shown in the example in this article. And that's where the true power of ranges lies, in the ability to chain various transformations together.
96
+
Range adaptors can be chained, which is where the power of ranges express itself.
95
97
96
98
## Range algorithms
97
99
98
-
Range algorithms have been introduced that take a range for an argument. `std::ranges::sort(myVector);` is an example.
99
-
100
-
The range algorithms are lazy, meaning that they operate on the range only when an element is accessed. They can work directly on a container, and are easily composed.
100
+
Range algorithms have been introduced that can take ranges for an argument. For example, `std::ranges::sort(myVector);`
101
101
102
-
Range-based algorithms are basically iterator-based algorithms where instead of taking two iterators, they take a range, instead.
102
+
The range algorithms are lazy, meaning that they operate on the range only when an element is accessed. They can work directly on a container, and can be easily chained together.
103
103
104
104
## Types of ranges
105
105
106
-
What you can do with a range depends on the underlying iterator type. Range concepts are named to reflect the traversal category its iterators support.
106
+
What you can do with a range depends on the underlying iterator type of the range. There are different kinds of ranges, called refinements. The different kinds of ranges are codified as C++ 20 concepts. This table lists various range concepts, along with the type of container they can be applied to:
107
107
108
-
There are different kinds, or refinements, of ranges, that are codified as concepts. This table lists them, along with the type of container they can be applied to:
109
-
j
110
108
| Range refinement | Description | Supported containers |
111
109
|--|--|--|
112
110
|`std::ranges::input_range`| Can iterate from beginning to end at least once |`std::forward_list`<br>`std::unordered_map`<br>`std::unordered_multimap`<br>`std::unordered_set`<br>`std::unordered_multiset`<br>`basic_istream_view`|
0 commit comments