Skip to content

Commit 025116b

Browse files
TylerMSFTTylerMSFT
authored andcommitted
get updates into right branch
1 parent eb1bf21 commit 025116b

4 files changed

Lines changed: 74 additions & 48 deletions

File tree

docs/standard-library/range-adaptors.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ ms.date: 11/2/2022
55
f1_keywords: ["ranges/std::all", "ranges/std::all_t", "ranges/std::common", "ranges/std::counted", "ranges/std::drop", "ranges/std::drop_while", "ranges/std::elements", "ranges/std::filter", "ranges/std::iota", "ranges/std::join", "ranges/std::keys", "ranges/std::lazy_split", "ranges/std::reverse", "ranges/std::split", "ranges/std::subrange", "ranges/std::take", "ranges/std::take_while", "ranges/std::transform"]
66
helpviewer_keywords: ["std::ranges [C++], all", "std::ranges [C++], all_t", "std::ranges [C++], common", "std::ranges [C++], counted", "std::ranges [C++], drop", "std::ranges [C++], drop_while", "std::ranges [C++], elements", "std::ranges [C++], filter", "std::ranges [C++], iota", "std::ranges [C++], join", "std::ranges [C++], keys", "std::ranges [C++], lazy_split", "std::ranges [C++], reverse", "std::ranges [C++], split", "std::ranges [C++], subrange", "std::ranges [C++], take", "std::ranges [C++], take_while", "std::ranges [C++], transform"]
77
---
8-
# Range adaptors
8+
# `ranges` adaptors
99

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

1212
A view is a lightweight object that refers to elements from a range. A view can:
1313

@@ -1503,4 +1503,5 @@ int main()
15031503
## See also
15041504

15051505
[`<ranges>`](ranges.md)\
1506+
[`<ranges>` concepts](range-concepts.md)\
15061507
[View classes](view-classes.md)

docs/standard-library/range-concepts.md

Lines changed: 63 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
description: "Learn more about range concepts and range iterator concepts."
33
title: "Concepts for ranges and range iterators"
44
ms.date: 11/02/2022
5-
f1_keywords: ["ranges/std::ranges::range"]
6-
helpviewer_keywords: ["std::ranges [C++], range"]
5+
f1_keywords: ["ranges/std::ranges::range", "ranges/std::ranges::bidirectional_range", "ranges/std::ranges::borrowed_range", "ranges/std::ranges::common_range", "ranges/std::ranges::contiguous_range", "ranges/std::ranges::forward_range", "ranges/std::ranges::input_range", "ranges/std::ranges::output_range", "ranges/std::ranges::random_access_range", "ranges/std::ranges::simple_view", "ranges/std::ranges::sized_range", "ranges/std::ranges::view", "ranges/std::ranges::viewable_range"]
6+
helpviewer_keywords: ["std::ranges [C++], ranges::range", "std::ranges [C++], ranges::bidirectional_range", "std::ranges [C++], ranges::borrowed_range", "std::ranges [C++], ranges::common_range", "std::ranges [C++], ranges::contiguous_range", "std::ranges [C++], ranges::forward_range", "std::ranges [C++], ranges::input_range", "std::ranges [C++], ranges::output_range", "std::ranges [C++], ranges::random_access_range", "std::ranges [C++], ranges::simple_view", "std::ranges [C++], ranges::sized_range", "std::ranges [C++], ranges::view", "std::ranges [C++], ranges::viewable_range"]
77
---
8-
# Concepts for `<ranges>`
8+
# `<ranges>` concepts
99

1010
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.
1111

@@ -15,7 +15,7 @@ Consider the following example, which defines a concept to prevent instantiating
1515
// requires /std:c++20 or later
1616
#include <iostream>
1717

18-
// Definition of the concept "dividable" that requires
18+
// Definition of dividable concept that requires
1919
// that arguments a & b of type T support division
2020
template <typename T>
2121
concept dividable = requires (T a, T b)
@@ -48,21 +48,38 @@ int main()
4848
}
4949
```
5050

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'
62+
example.cpp(31): error C2780: 'DivideEmUp<T> DivideEmUp(DivideEmUp<T>)': expects 1 arguments - 0 provided
63+
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+
5168
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.
5269

53-
| **Range concept ** | **Description** |
70+
| Range concept | Description |
5471
|--|--|
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. |
5673
| [`bidirectional_range`](#bidirectional_range)<sup>C++20</sup> | Supports reading and writing forwards and backwards. |
5774
| [`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. |
60-
| [`forward_range`](#forward_range)<sup>C++20</sup> | Supports reading multiple times. |
61-
| [`input_range`](#input_range)<sup>C++20</sup> | Supports reading. |
75+
| [`common_range`](#common_range)<sup>C++20</sup> | The type of the iterator and the type of the sentinel are the same. |
76+
| [`contiguous_range`](#contiguous_range)<sup>C++20</sup> | The elements are sequential in memory and can be accessed by using pointer arithmetic. |
77+
| [`forward_range`](#forward_range)<sup>C++20</sup> | Supports reading (and possibly writing) a range multiple times. |
78+
| [`input_range`](#input_range)<sup>C++20</sup> | Supports reading at least once. |
6279
| [`output_range`](#output_range)<sup>C++20</sup> | Supports writing. |
6380
| [`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. |
6683
| [`view`](#view)<sup>C++20</sup> | Has efficient (constant time) move construction, assignment, and destruction. |
6784
| [`viewable_range`](#viewable_range)<sup>C++20</sup> | A type that either is a view or can be converted to one. |
6885

@@ -89,7 +106,7 @@ Some examples of a `bidirectional_range` are `std::set`, `std::vector`, and `std
89106

90107
## `borrowed_range`
91108

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

94111
```cpp
95112
template<class T>
@@ -105,7 +122,7 @@ The type to test to see if it's a `borrowed_range`.
105122

106123
## `common_range`
107124

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

110127
```cpp
111128
template<class T>
@@ -120,13 +137,13 @@ The type to test to see if it's a `common_range`.
120137

121138
### Remarks
122139

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

125142
The standard containers (for example, `vector`) meet the requirements of `common_range`.
126143

127144
## `contiguous_range`
128145

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

131148
```cpp
132149
template<class T>
@@ -146,7 +163,7 @@ A `contiguous_range` can be accessed by pointer arithmetic because the elements
146163
147164
Some examples of a `contiguous_range` are `std::array`, `std::vector`, and `std::string`.
148165
149-
### Example `contiguous_range`
166+
### Example: `contiguous_range`
150167
151168
The following example shows using pointer arithmetic to access a `contiguous_range`:
152169
@@ -160,7 +177,7 @@ int main()
160177
{
161178
// Show that vector is a contiguous_range
162179
std::vector<int> v = {0,1,2,3,4,5};
163-
std::cout << std::boolalpha << std::ranges::contiguous_range<decltype(v)> << '\n'; // outputs "true"
180+
std::cout << std::boolalpha << std::ranges::contiguous_range<decltype(v)> << '\n'; // outputs true
164181
165182
// Show that pointer arithmetic can be used to access the elements of a contiguous_range
166183
auto ptr = v.data();
@@ -176,7 +193,7 @@ true
176193

177194
## `forward_range`
178195

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

181198
```cpp
182199
template<class T>
@@ -210,7 +227,7 @@ The type to test to see if it's an `input_range`.
210227

211228
When a type meets the requirements of `input_range`:
212229

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.
214231
- 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.
215232
- It can be used with `ranges::for_each`.
216233
- 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`.
253270

254271
### Remarks
255272

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

258275
Some examples of a `random_access_range` are `std::vector`, `std::array`, and `std::deque`.
259276

260277
## `range`
261278

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

264281
```cpp
265-
template<class R>
282+
template<class T>
266283
concept range = requires(T& rg)
267284
{
268285
ranges::begin(rg);
@@ -280,7 +297,7 @@ The type to test to see if it's a `range`.
280297
The requirements of a `range` are:
281298
- It can be iterated using `std::ranges::begin()` and `std::ranges::end()`
282299
- `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.
284301

285302
## `Simple_View`
286303

@@ -301,7 +318,10 @@ The type to test to see if it's a `Simple_View`.
301318

302319
### Remarks
303320

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

306326
## `sized_range`
307327

@@ -327,7 +347,7 @@ The requirements of a `sized_range` are that calling `ranges::size` on it:
327347
328348
Some examples of a `sized_range` are `std::list` and `std::vector`.
329349
330-
### Example `sized_range`
350+
### Example: `sized_range`
331351
332352
The following example shows that a `vector` of `int` is a `sized_range`:
333353
@@ -345,9 +365,9 @@ int main()
345365

346366
## `view`
347367

348-
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 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.
349369

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

352372
```cpp
353373
template<class T>
@@ -357,22 +377,26 @@ concept view = ranges::range<T> && std::movable<T> && ranges::enable_view<T>;
357377
### Parameters
358378

359379
*`T`*\
360-
The type to test to see if it's a composable `view`.
380+
The type to test to see if it's a view.
361381

362382
### Remarks
363383

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

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

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

373397
## `viewable_range`
374398

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

377401
```cpp
378402
template<class T>
@@ -383,14 +407,14 @@ template<class T>
383407
### Parameters
384408

385409
*`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.
387411

388412
### Remarks
389413

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

392416
## See also
393417

394418
[`<ranges>`](ranges.md)\
395-
[Range adaptors](range-adaptors.md)\
419+
[`<ranges> adaptors](range-adaptors.md)\
396420
[View classes](view-classes.md)

docs/standard-library/ranges.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ With ranges, you can call `std::ranges::sort(myVector);`, which is treated as if
1616

1717
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.
1818

19-
## An example of ranges
19+
## A ranges example
2020

2121
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:
2222

@@ -95,7 +95,7 @@ For more information about creating views, see [Range adaptors](range-adaptors.m
9595

9696
Some range algorithms take a range argument. An example is `std::ranges::sort(myVector);`.
9797

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

100100
## `<ranges>` functions
101101

@@ -133,6 +133,7 @@ The range concepts mirror the hierarchy of iterator categories. The following ta
133133

134134
## See also
135135

136-
[Range functions](range-functions.md)\
137-
[Range adaptors](range-adaptors.md)\
136+
[`<ranges>` functions](range-functions.md)\
137+
[`<ranges>` adaptors](range-adaptors.md)\
138+
[`<ranges>` concepts](range-concepts.md)\
138139
[Header files reference](../standard-library/cpp-standard-library-header-files.md)

0 commit comments

Comments
 (0)