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
Copy file name to clipboardExpand all lines: docs/standard-library/ranges.md
+23-28Lines changed: 23 additions & 28 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,24 +1,22 @@
1
1
---
2
2
title: "<ranges>"
3
-
description: "API reference for the Standard Template Library (STL) ranges namespace, which provides ... JTW"
3
+
description: "Overview of the Standard Template Library (STL) ranges library"
4
4
ms.date: "04/13/2021"
5
5
f1_keywords: ["<ranges>"]
6
6
helpviewer_keywords: ["ranges header"]
7
7
---
8
8
9
9
# `<ranges>`
10
10
11
-
## Overview
12
-
13
11
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).
14
12
15
-
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'd just like 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 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.
16
14
17
-
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, STL algorithms take ranges as parameters, instead of iterators. 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, 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`.
18
16
19
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.
20
18
21
-
Unlike an iterator, ranges are copyable and assignable. Because they don't own the elements like a container does, they are lightweight.
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; });
38
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.
39
38
40
-
*** Note: I'd like to write something like this but it doesn't work:
41
-
```cpp
42
-
std::ranges::copy( input, std::views::filter([](const int n) {return n % 3 == 0; })
43
-
| std::views::filter([](const int n) {return n * n; }),
44
-
std::back_inserter(output));
45
-
```
46
-
47
-
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).
39
+
In addition to being a little easier to read, it avoid the memory allocation required for `intermediate` in the non-ranges example.
48
40
49
-
The '`|`' symbol chains the range view operations together, and is read left to right.
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.
50
42
51
43
The result, `output`, is itself a type of range called a view, which is discussed next.
52
44
@@ -58,24 +50,27 @@ A view is lightweight. Like a range, a view doesn't own the elements it provides
58
50
59
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.
60
52
61
-
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 line `auto x = ...`, you'll hit that breakpoint each iteration through the range `for` loop. That's because it isn't until the element is accessed that the operation that define the view are evaluated for that element.
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.
auto divisible_by_three = [](const int n) {return n % 3 == 0; };
65
+
auto square = [](const int n) {return n * n; };
71
66
72
-
auto x = input | std::views::filter([](const int n) {return n % 3 == 0; })
73
-
| std::views::transform([](const int n) {return n * n; });
67
+
auto x = input | std::views::filter(divisible_by_three)
68
+
| std::views::transform(square);
74
69
75
70
for (int i : x)
76
-
{
77
-
std::cout << i << '\n';
78
-
}
71
+
{
72
+
std::cout << i << '\n';
73
+
}
79
74
return 0;
80
75
}
81
76
```
@@ -84,7 +79,7 @@ int main()
84
79
85
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.
86
81
87
-
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 range acts like an iterator that takes the elements divisible by three, and provides their square.
82
+
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.
88
83
89
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.
90
85
@@ -111,12 +106,12 @@ Range-based algorithms are basically iterator-based algorithms where instead of
111
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.
112
107
113
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:
114
-
109
+
j
115
110
| Range refinement | Description | Supported containers |
116
111
|--|--|--|
117
-
|`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`|
112
+
|`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`|
118
113
|`std::ranges::output_range `| A range whose iterator type satisfies output_iterator | ? |
119
-
|`std::ranges::forward_range`| Can iterate from beginning to end more than once) |?|
114
+
|`std::ranges::forward_range`| Can iterate from beginning to end more than once) |`std::forward_list`<br>`std::unordered_map`<br>`std::unordered_multimap`<br>`std::unordered_set`<br>`std::unordered_multiset`|
120
115
|`std::ranges::bidirectional_range`| Can iterate forward and backward more than once |`std::list`<br>`std::map`<br>`std::multimap`<br>`std::multiset`<br>`std::set`|
121
116
|`std::ranges::random_access_range`| Can access an arbitrary element (in constant time) using the `[]` operator) |`std::deque`|
122
117
|`std::ranges::contiguous_range`| The elements are stored in memory consecutively |`std::array`<br>`std::string`<br>`std::vector`|
0 commit comments