Skip to content

Commit a429f23

Browse files
TylerMSFTTylerMSFT
authored andcommitted
incorp some review
1 parent 88a856b commit a429f23

1 file changed

Lines changed: 23 additions & 28 deletions

File tree

docs/standard-library/ranges.md

Lines changed: 23 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,22 @@
11
---
22
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"
44
ms.date: "04/13/2021"
55
f1_keywords: ["<ranges>"]
66
helpviewer_keywords: ["ranges header"]
77
---
88

99
# `<ranges>`
1010

11-
## Overview
12-
1311
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).
1412

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.
1614

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`.
1816

1917
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.
2018

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.
2220

2321
## An example
2422

@@ -32,21 +30,15 @@ std::transform(intermediate.begin(), intermediate.end(), std::back_inserter(outp
3230
With ranges, you can instead write this as:
3331
3432
```cpp
33+
// requires /std:c++latest
3534
std::vector<int> input = { 1, 2, 3, 5, 8, 13, 21, 34, 45, 79 };
36-
vector<int> output;
3735
auto output = input | std::views::filter([](const int n) {return n % 3 == 0; }) | std::views::transform([](const int n) {return n * n; });
3836
```
37+
> [!NOTE] The ranges examples need to be compiled with the [`/std:c++latest`](../build/reference/std-specify-language-standard-version.md) compiler option.
3938
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.
4840

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.
5042

5143
The result, `output`, is itself a type of range called a view, which is discussed next.
5244

@@ -58,24 +50,27 @@ A view is lightweight. Like a range, a view doesn't own the elements it provides
5850

5951
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.
6052

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.
6254

6355
```cpp
64-
#include <iostream>
56+
// requires /std:c++latest
6557
#include <ranges>
6658
#include <vector>
59+
#include <iostream>
6760

6861
int main()
6962
{
70-
std::vector<int> input = { 1, 2, 3, 5, 8, 13, 21, 34, 45, 79 };
63+
std::vector<int> input = { 1, 2, 3, 5, 8, 13, 21, 34, 45, 79 };
64+
auto divisible_by_three = [](const int n) {return n % 3 == 0; };
65+
auto square = [](const int n) {return n * n; };
7166

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);
7469

7570
for (int i : x)
76-
{
77-
std::cout << i << '\n';
78-
}
71+
{
72+
std::cout << i << '\n';
73+
}
7974
return 0;
8075
}
8176
```
@@ -84,7 +79,7 @@ int main()
8479

8580
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.
8681

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.
8883

8984
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.
9085

@@ -111,12 +106,12 @@ Range-based algorithms are basically iterator-based algorithms where instead of
111106
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.
112107

113108
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
115110
| Range refinement | Description | Supported containers |
116111
|--|--|--|
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` |
118113
| `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` |
120115
| `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`|
121116
| `std::ranges::random_access_range` | Can access an arbitrary element (in constant time) using the `[]` operator) | `std::deque` |
122117
| `std::ranges::contiguous_range` | The elements are stored in memory consecutively | `std::array`<br>`std::string`<br>`std::vector` |

0 commit comments

Comments
 (0)