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
Range adaptors create a *view* (one of the [view classes](view-classes.md) in the `std::views` namespace) from a range. We recommend that you use an adaptor in `std::ranges::views` instead of creating the view types directly. The adaptors are the intended way to access views. They're easier to use, and in some cases more efficient, than creating instances of the view types directly.
10
+
Range adaptors create a *view* (one of the [View classes](view-classes.md) in the `std::views` namespace) from a range. We recommend that you use an adaptor in `std::ranges::views` instead of creating the view types directly. The adaptors are the intended way to access views. They're easier to use, and in some cases more efficient, than creating instances of the view types directly.
11
11
12
12
A view is a lightweight object that refers to elements from a range. A view can:
Concepts are a C++20 language feature that constrain template parameters at compile time. They help prevent incorrect template instantiations, convey template argument requirements in a readable form, and provide more succinct template related compiler errors.
11
11
@@ -15,7 +15,7 @@ Consider the following example, which defines a concept to prevent instantiating
15
15
// requires /std:c++20 or later
16
16
#include<iostream>
17
17
18
-
// Definition of the concept "dividable" that requires
18
+
// Definition of dividable concept that requires
19
19
// that arguments a & b of type T support division
20
20
template <typename T>
21
21
concept dividable = requires (T a, T b)
@@ -48,21 +48,38 @@ int main()
48
48
}
49
49
```
50
50
51
+
Using Visual Studio 2022, version 17.4p4, the errors generated for the preceding example are:
52
+
53
+
```output
54
+
example.cpp
55
+
example.cpp(31): error C7602: 'DivideEmUp': the associated constraints are not satisfied
56
+
example.cpp(18): note: see declaration of 'DivideEmUp'
57
+
example.cpp(16): note: the concept 'dividable<char*>' evaluated to false
58
+
example.cpp(9): note: the expression is invalid
59
+
example.cpp(31): error C2641: cannot deduce template arguments for 'DivideEmUp'
60
+
example.cpp(31): error C2783: 'DivideEmUp<T> DivideEmUp(void)': could not deduce template argument for 'T'
61
+
example.cpp(18): note: see declaration of 'DivideEmUp'
example.cpp(18): note: see declaration of 'DivideEmUp'
64
+
```
65
+
66
+
If you specify the compiler switch `/diagnostics:caret`, then the errors generated notably includes concept `dividable<char*>` evaluated to false and even points directly to the expression requirement `(a / b)` that failed.
67
+
51
68
The following concepts are defined in `std::ranges` and are declared in the `<ranges>` header file. They're used in the declarations of [range adaptors](range-adaptors.md), [views](view-classes.md), and so on.
52
69
53
-
|**Range concept **|**Description**|
70
+
| Range concept |Description |
54
71
|--|--|
55
-
|[`range`](#range)<sup>C++20</sup> | A type that can be iterated. |
72
+
|[`range`](#range)<sup>C++20</sup> | A type that provides an iterator and a sentinel. |
56
73
|[`bidirectional_range`](#bidirectional_range)<sup>C++20</sup> | Supports reading and writing forwards and backwards. |
57
74
|[`borrowed_range`](#borrowed_range)<sup>C++20</sup> | The lifetime of the type's iterators aren't tied to the object's lifetime. |
58
-
|[`common_range`](#common_range)<sup>C++20</sup> | The type of the iterator and the sentinel are the same. |
59
-
|[`contiguous_range`](#contiguous_range)<sup>C++20</sup> | The elements are sequential in memory and can be accessed by index. |
|[`random_access_range`](#random_access_range)<sup>C++20</sup> | Supports reading and writing by index. |
64
-
|[`simple_view`](#simple_view)<sup>C++20</sup> | Not an official concept defined as part of the standard library, but used as a 'helper' concept on some interfaces. |
65
-
|[`sized_range`](#sized_range)<sup>C++20</sup> | Provides the number of elements efficiently. |
81
+
|[`Simple_View`](#simple_view)<sup>C++20</sup> | Not an official concept defined as part of the standard library, but used as a helper concept on some interfaces. |
82
+
|[`sized_range`](#sized_range)<sup>C++20</sup> | Provides the number of elements in a range efficiently. |
66
83
|[`view`](#view)<sup>C++20</sup> | Has efficient (constant time) move construction, assignment, and destruction. |
67
84
|[`viewable_range`](#viewable_range)<sup>C++20</sup> | A type that either is a view or can be converted to one. |
68
85
@@ -89,7 +106,7 @@ Some examples of a `bidirectional_range` are `std::set`, `std::vector`, and `std
89
106
90
107
## `borrowed_range`
91
108
92
-
A type models `borrowed_range` if the validity of iterators you get from the object can outlive the lifetime of the object.
109
+
A type models `borrowed_range` if the validity of iterators you get from the object can outlive the lifetime of the object. That is, the iterators for a range can be used even when the range no longer exists.
93
110
94
111
```cpp
95
112
template<classT>
@@ -105,7 +122,7 @@ The type to test to see if it's a `borrowed_range`.
105
122
106
123
## `common_range`
107
124
108
-
The type of the iterator for a `common_range` is the same type as the sentinel type. That is, the types returned from `begin()` and `end()`are the same.
125
+
The type of the iterator for a `common_range` is the same as the type of the sentinel. That is, `begin()` and `end()`return the same type.
109
126
110
127
```cpp
111
128
template<classT>
@@ -120,13 +137,13 @@ The type to test to see if it's a `common_range`.
120
137
121
138
### Remarks
122
139
123
-
Providing the same iterator type from `std::ranges::begin()` and `std::ranges::end()` is important for algorithms that calculate the distance between two iterators and for algorithms that accept ranges denoted by iterator pairs.
140
+
Getting the type from `std::ranges::begin()` and `std::ranges::end()` is important for algorithms that calculate the distance between two iterators, and for algorithms that accept ranges denoted by iterator pairs.
124
141
125
142
The standard containers (for example, `vector`) meet the requirements of `common_range`.
126
143
127
144
## `contiguous_range`
128
145
129
-
The elements of a `contiguous_range` are laid out sequentially in memory and can be accessed using pointer arithmetic. An array is a `contiguous_range`, for example.
146
+
The elements of a `contiguous_range` are stored sequentially in memory and can be accessed using pointer arithmetic. For example, an array is a `contiguous_range`.
130
147
131
148
```cpp
132
149
template<classT>
@@ -146,7 +163,7 @@ A `contiguous_range` can be accessed by pointer arithmetic because the elements
146
163
147
164
Some examples of a `contiguous_range` are `std::array`, `std::vector`, and `std::string`.
148
165
149
-
### Example `contiguous_range`
166
+
### Example: `contiguous_range`
150
167
151
168
The following example shows using pointer arithmetic to access a `contiguous_range`:
// Show that pointer arithmetic can be used to access the elements of a contiguous_range
166
183
auto ptr = v.data();
@@ -176,7 +193,7 @@ true
176
193
177
194
## `forward_range`
178
195
179
-
A `forward_range` supports reading and possibly writing a `range` multiple times.
196
+
A `forward_range` supports reading (and possibly writing) a `range` multiple times.
180
197
181
198
```cpp
182
199
template<classT>
@@ -210,7 +227,7 @@ The type to test to see if it's an `input_range`.
210
227
211
228
When a type meets the requirements of `input_range`:
212
229
213
-
- The `ranges::begin()` function returns an `input_iterator`. Calling `begin()` more than once for an `input_range`has undefined behavior.
230
+
- The `ranges::begin()` function returns an `input_iterator`. Calling `begin()` more than once on an `input_range`results in undefined behavior.
214
231
- You can dereference an `input_iterator` repeatedly, which yields the same value each time. An `input_range` isn't multi-pass. Incrementing an iterator invalidates any copies.
215
232
- It can be used with `ranges::for_each`.
216
233
- It *at least* has an `input_iterator`. It may have a more capable iterator type.
@@ -253,16 +270,16 @@ The type to test to see if it's a `sized_range`.
253
270
254
271
### Remarks
255
272
256
-
A `random_access_range`provides the most flexible iterator. It provides the capabilities of an `input_range`, `output_range`, `forward_range`, and `bidirectional_range`. A `random_access_range` is also sortable.
273
+
A `random_access_range`is the most flexible iterator. It has the capabilities of an `input_range`, `output_range`, `forward_range`, and `bidirectional_range`. A `random_access_range` is also sortable.
257
274
258
275
Some examples of a `random_access_range` are `std::vector`, `std::array`, and `std::deque`.
259
276
260
277
## `range`
261
278
262
-
Defines the requirements a type must meet to be a `range`. A `range` provides an iterator and a sentinel for iterating over its elements.
279
+
Defines the requirements a type must meet to be a `range`. A `range` provides an iterator and a sentinel, so that you can iterate over its elements.
263
280
264
281
```cpp
265
-
template<classR>
282
+
template<classT>
266
283
concept range = requires(T& rg)
267
284
{
268
285
ranges::begin(rg);
@@ -280,7 +297,7 @@ The type to test to see if it's a `range`.
280
297
The requirements of a `range` are:
281
298
- It can be iterated using `std::ranges::begin()` and `std::ranges::end()`
282
299
-`ranges::begin()` and `ranges::end()` run in amortized constant time and don't modify the `range`. Amortized constant time doesn't mean O(1), but that the average cost over a series of calls, even in the worst case, is O(n) rather than O(n^2) or worse.
283
-
-\[`ranges::begin()`, `ranges::end()`) denotes a valid range.
300
+
-`[ranges::begin(), ranges::end())` denotes a valid range.
284
301
285
302
## `Simple_View`
286
303
@@ -301,7 +318,10 @@ The type to test to see if it's a `Simple_View`.
301
318
302
319
### Remarks
303
320
304
-
A `view V` is a [`Simple_View`](#simple_view) if `V` is a `view`, `const V` is a `range`, and both `v` and `const V` have the same iterator and sentinel types.
321
+
A view `V` is a [`Simple_View`](#simple_view) if all of the following are true:
322
+
-`V` is a view
323
+
-`const V` is a range
324
+
- Both `v` and `const V` have the same iterator and sentinel types.
305
325
306
326
## `sized_range`
307
327
@@ -327,7 +347,7 @@ The requirements of a `sized_range` are that calling `ranges::size` on it:
327
347
328
348
Some examples of a `sized_range` are `std::list` and `std::vector`.
329
349
330
-
### Example `sized_range`
350
+
### Example: `sized_range`
331
351
332
352
The following example shows that a `vector` of `int` is a `sized_range`:
333
353
@@ -345,9 +365,9 @@ int main()
345
365
346
366
## `view`
347
367
348
-
A `view` has constant time move construction, assignment, and destruction operationsregardless of the number of elements it has. Views don't need to be copy constructible or copy assignable, but if they are those operations must also be constant time.
368
+
A `view` has constant time move construction, assignment, and destruction operations--regardless of the number of elements it has. Views don't need to be copy constructible or copy assignable, but if they are, those operations must also run in constant time.
349
369
350
-
Because of this performance in constant time, views can be efficiently composed. For example, given a vector of `int` called `input`, a lambda that determines if a number is divisible by three, and a lambda that squares a number, the statement `auto x = input | std::views::filter(divisible_by_three) | std::views::transform(square);` efficiently produces a view that contains the squares of the numbers in input that are divisible by three. Connecting views together with `|` is referred to as composing the views. If a type satisfies the `view` concept, then it meets the requirements of being efficiently composable.
370
+
Because of the constant time requirement, you can efficiently compose views. For example, given a vector of `int` called `input`, a function that determines if a number is divisible by three, and a function that squares a number, the statement `auto x = input | std::views::filter(divisible_by_three) | std::views::transform(square);` efficiently produces a view that contains the squares of the numbers in input that are divisible by three. Connecting views together with `|` is referred to as composing the views. If a type satisfies the [`view`](range-concepts.md#view) concept, then it can be composed efficiently.
The type to test to see if it's a composable `view`.
380
+
The type to test to see if it's a view.
361
381
362
382
### Remarks
363
383
364
-
The essential requirement that makes a `view` composable is that it's cheap to move/copy. This is because the `view` is moved/copied when it's composed with another `view`. It must be a movable `range`.
384
+
The essential requirement that makes a view composable is that it's cheap to move/copy. This is because the view is moved/copied when it's composed with another view. It must be a movable `range`.
385
+
386
+
`ranges::enable_view<T>` is a trait used to claim conformance to the semantic requirements of the `view` concept. A type can opt-in by:
387
+
- publicly and unambiguously deriving from a specialization of `ranges::view_interface`
388
+
- publicly and unambiguously deriving from the empty class `ranges::view_base`, or
389
+
- specializing `ranges::enable_view<T>` to `true`
390
+
391
+
Option 1 is generally preferred because `view_interface` also provides default implementation that save some boilerplate code you have to write.
365
392
366
-
`ranges::enable_view<T>` is a trait used to claim conformance to the semantic requirements of the `view` concept. A type can "opt-in" by:
367
-
1. publicly and unambiguously deriving from a specialization of `ranges::view_interface`
368
-
2. publicly and unambiguously deriving from the empty class `ranges::view_base`, or
369
-
3. specializing `ranges::enable_view<T>` to `true`
393
+
Failing that, option 2 is a little simpler than option 3.
370
394
371
-
Option 1 is generally preferred because `view_interface` also provides default implementations that save some boilerplate code you have to write. Failing that, option 2 is a little simpler than option 3. The advantage of option 3 is that it's possible without changing the definition of the type.
395
+
The advantage of option 3 is that it's possible without changing the definition of the type.
372
396
373
397
## `viewable_range`
374
398
375
-
A `viewable_range` is a type that either is a `view` or that can be converted to one.
399
+
A `viewable_range` is a type that either is a view or can be converted to one.
376
400
377
401
```cpp
378
402
template<classT>
@@ -383,14 +407,14 @@ template<class T>
383
407
### Parameters
384
408
385
409
*`T`*\
386
-
The type to test to see if it either is a `view` or can be converted to one.
410
+
The type to test to see if it either is a view or can be converted to one.
387
411
388
412
### Remarks
389
413
390
-
Use `std::ranges::views::all()` to convert a `range` to a `view`.
414
+
Use `std::ranges::views::all()` to convert a range to a view.
Copy file name to clipboardExpand all lines: docs/standard-library/ranges.md
+5-4Lines changed: 5 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -16,7 +16,7 @@ With ranges, you can call `std::ranges::sort(myVector);`, which is treated as if
16
16
17
17
But perhaps the most important benefit of ranges is that you can compose STL algorithms that operate on ranges in a style that's reminiscent of functional programming.
18
18
19
-
## An example of ranges
19
+
## A ranges example
20
20
21
21
Before ranges, if you wanted to transform the elements of a collection that met a certain criterion, you needed to introduce an intermediate step to hold the results between operations. For example, if you wanted to build a vector of squares from the elements in another vector that are divisible by three, you could write something like:
22
22
@@ -95,7 +95,7 @@ For more information about creating views, see [Range adaptors](range-adaptors.m
95
95
96
96
Some range algorithms take a range argument. An example is `std::ranges::sort(myVector);`.
97
97
98
-
The range algorithms are almost identical to the corresponding iterator-pair algorithms in the `std` namespace. The difference is that they have concept-enforced constraints, and they accept either range arguments or more iterator-sentinel argument pairs. They can work directly on a container and can be easily chained together.
98
+
The range algorithms are almost identical to the corresponding iterator-pair algorithms in the `std` namespace. The difference is that they have [concept-enforced constraints](range-concepts.md), and they accept either range arguments or more iterator-sentinel argument pairs. They can work directly on a container and can be easily chained together.
99
99
100
100
## `<ranges>` functions
101
101
@@ -133,6 +133,7 @@ The range concepts mirror the hierarchy of iterator categories. The following ta
0 commit comments