diff --git a/docs/standard-library/range-adaptors.md b/docs/standard-library/range-adaptors.md index 086889545a6..c3f56cfe75d 100644 --- a/docs/standard-library/range-adaptors.md +++ b/docs/standard-library/range-adaptors.md @@ -7,11 +7,16 @@ helpviewer_keywords: ["std::ranges [C++], all", "std::ranges [C++], all_t", "std --- # Range adaptors -Range adaptors create a *view* (one of the [view classes](view-classes.md) in the `std::views` namespace) from a range. Prefer using an adaptor in `std::ranges::views` instead of creating the view types directly. The adaptors are the intended way to access views, are easier to use, and in some cases are more efficient than creating instances of the view types directly. +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. -A *view* is a lightweight object that refers to elements from a range. A view can consist of only certain elements from a range, or it can represent a transformation of elements from a range, or it can be the reverse of, or only the first `n` elements of, a range, and so on. It can also be the result of a combination of those things. +A view is a lightweight object that refers to elements from a range. A view can: -A view is cheap, O(1), to copy, assign, and destroy--no matter how many elements are involved. Consider the following example: +- Consist of only certain elements from a range. +- Represent a transformation of elements from a range. +- Be the reverse of, or only the first `n` elements of, a range. +- Be a combination of the preceding things. + +A view is cheap, `O(1)`, to copy, assign, and destroy--no matter how many elements are involved. Consider the following example: ```cpp // requires /std:c++20 or later @@ -39,11 +44,13 @@ int main() 0 9 36 81 ``` -The first range adaptor, `std::views::filter` provides a view that contains the elements from `input` that are divisible by three. The other range adaptor, `std::views::transform`, takes the view containing the elements divisible by three, and provides a view of the square of those elements. +The first range adaptor, `std::views::filter`, provides a view that contains the elements from `input` that are divisible by three. The other range adaptor, `std::views::transform`, takes the view that contains the elements divisible by three and provides a view of the square of those elements. + +When a range adaptor produces a view, it doesn't incur the cost of transforming every element in the range to produce that view. The cost to process an element in the view is paid only when you access that element. -When a range adaptor produces a view, it doesn't incur the cost of transforming every element in the range to produce that view. The cost to process an element in the view is only paid when you access that element. Creating a view only prepares to do work in the future. In the previous example, creating the view doesn't result in finding all the elements divisible by three, nor does it square the elements it finds. That work only happens when you access an element in the view. +Creating a view only prepares to do work in the future. In the previous example, creating the view doesn't result in finding all the elements divisible by three. It also doesn't square the elements that it finds. That work happens only when you access an element in the view. -Elements of a view are usually the actual elements of the range used to create the view. The view usually doesn't own the elements (`owning_view` is an exception), it just refers to them. Changing an element changes that element in the range the view was created from. The following example shows this behavior: +Elements of a view are usually the actual elements of the range that are used to create the view. The view usually doesn't own the elements (`owning_view` is an exception); it just refers to them. Changing an element changes that element in the range that the view was created from. The following example shows this behavior: ```cpp #include @@ -70,41 +77,45 @@ int main() } ``` -Range adaptors come in many forms. There are range adaptors that allow you to produce a view by filtering another range based on a predicate (`view::filter`), transforming the elements in a range (`view::transform`), splitting a range (`view::split`), and more. +Range adaptors come in many forms. For example, there are range adaptors that allow you to produce a view by: + +- Filtering another range based on a predicate (`view::filter`). +- Transforming the elements in a range (`view::transform`). +- Splitting a range (`view::split`). -Range adaptors can be chained together (composed), which is where the power and flexibility of ranges is most apparent. Composing range adaptors allows you to overcome a core problem with the previous STL algorithms, which is that they aren't easy to chain together (compose). +Range adaptors can be chained together (composed). That's where the power and flexibility of ranges are most apparent. Composing range adaptors allows you to overcome a core problem with the previous Standard Template Library (STL) algorithms, which is that they aren't easy to chain together. -The following range adaptors are available in the `std::views` namespace. The `std::views`namespace is a convenience alias for `std::ranges::views`. +The following range adaptors are available in the `std::views` namespace. The `std::views` namespace is a convenience alias for `std::ranges::views`. -| **Range adaptor** | **Description** | +| Range adaptor | Description | |--|--| | [`all`](#all)C++20 | Create a view that refers to a range and its elements. | | [`common`](#common)C++20 | Create a view that has the same iterator and sentinel types from a range that doesn't. | | [`counted`](#counted)C++20 | Create a view of the first *n* elements of a range, starting from the specified location. | | [`drop`](#drop)C++20 | Create a view from another view, skipping the specified number of elements from the front. | -| [`drop_while`](#drop_while)C++20 | Create a view that contains the elements of a range that remain once the leading elements that match the specified condition are dropped. | +| [`drop_while`](#drop_while)C++20 | Create a view that contains the elements of a range that remain after the leading elements that match the specified condition are dropped. | | [`elements`](#elements)C++20 | Create a view of the selected index into each tuple-like value in a range. | | [`filter`](#filter)C++20 | Create a view that contains the elements of a range that match the specified condition. | | [`iota`](#iota)C++20 | Create a view that contains a sequence of increasing values. | | [`keys`](#keys)C++20 | Create a view of the first index into each tuple-like value in a collection. | | [`lazy_split`](#lazy_split)C++20 | Split a view into subranges based on a delimiter. | | [`reverse`](#reverse)C++20 | Create a view of the elements of a range in reverse order. | -| [`single`](#single)C++20 | Create a view containing one element. | +| [`single`](#single)C++20 | Create a view that contains one element. | | [`split`](#split)C++20 | Split a view into subranges based on a delimiter. | -| [`take`](#take)C++20 | Create a view of the first *N* elements from another view. | +| [`take`](#take)C++20 | Create a view of the first *n* elements from another view. | | [`take_while`](#take_while)C++20 | Create a view that contains the leading elements of a range that match the specified condition. | | [`transform`](#transform)C++20 | Create a view of transformed elements from another view. | | [`values`](#values)C++20 | Create a view of the second index into each tuple-like value in a collection. | In the previous table, a range adaptor is typically described as taking a range and producing a view. To be precise, range adaptors have a range argument that accepts one of the following: -- The cv-unqualified type models `view` and the argument is an rvalue or is copyable. +- The `cv-unqualified` type models `view`, and the argument is an rvalue or is copyable. - When you pass the argument as an lvalue, it must model `range` and live as long as the view. - When you pass the argument as an rvalue, such as when calling [`owning_view`](owning-view-class.md), it must model `range` and `movable`. Range adaptor functions are typically [function objects](https://eel.is/c++draft/function.objects), which look like function calls and enforce constraints on the types that can be passed. -You can pass range adaptors and the result of pipe operations (`|`) to code that expects function objects. In the following example, the view created by the `split` range adaptor is passed to the `transform` range adaptor as if by function a call because the `transform` range adaptor is a function object. +You can pass range adaptors and the result of pipe operations (`|`) to code that expects function objects. In the following example, the view that the `split` range adaptor creates is passed to the `transform` range adaptor as if by a function call, because the `transform` range adaptor is a function object. ```cpp std::map x = {{0, "Hello, world"}, {42, "Goodbye, world"}}; @@ -124,26 +135,27 @@ constexpr ranges::view auto all(R&& rg) const noexcept; ### Parameters -*`R`*\ +`R`\ The type of the underlying range. -*`rg`*\ +`rg`\ The range to create the view from. ### Return value -- If *`rg`* is already a view, a copy of *`rg`*. -- If *`rg`* is a non-view lvalue, a [`ref_view`](ref-view-class.md) that refers to *`rg`* (the lifetime of the view is tied to the lifetime of *`rg`*). -- If *`rg`* is a non-view rvalue such as a temporary object, or is the result of passing the range to `std::move`, an [`owning_view`](owning-view-class.md). +- If `rg` is already a view, a copy of `rg`. +- If `rg` is a non-view lvalue, a [`ref_view`](ref-view-class.md) that refers to `rg`. (The lifetime of the view is tied to the lifetime of `rg`.) +- If `rg` is a non-view rvalue such as a temporary object, or is the result of passing the range to `std::move`, an [`owning_view`](owning-view-class.md). Use `std::views::all_t` to get the type of the returned view. ### Remarks -This range adaptor is the best way to convert a range into a view. One reason to create a view from a range is to pass it by value at low cost if passing the range by value could be expensive.\ -Getting a view for a range is a useful alternative to passing a heavyweight range by value because views are inexpensive to create, copy, and destroy. A possible exception is the `owning_view`, which is a view that owns the underlying range. +This range adaptor is the best way to convert a range into a view. One reason to create a view from a range is to pass it by value at low cost, if passing the range by value could be expensive. -In general, the worst case scenario for destroying a view has O(N) complexity for the number of elements in the range. Even if you destroy `K` copies of view with `N` elements, the total complexity is still O(N) because the underlying range is destroyed only once. +Getting a view for a range is a useful alternative to passing a heavyweight range by value because views are inexpensive to create, copy, and destroy. A possible exception is `owning_view`, which is a view that owns the underlying range. + +In general, the worst-case scenario for destroying a view has `O(N)` complexity for the number of elements in the range. Even if you destroy `K` copies of view with `N` elements, the total complexity is still `O(N)` because the underlying range is destroyed only once. ### Example: `all` @@ -184,20 +196,20 @@ constexpr ranges::view auto common(R&& rg) const noexcept; ### Parameters -*`R`*\ +`R`\ The type of the underlying range. -*`rg`*\ +`rg`\ The range to create the view from. ### Return value -- `views::all(rg)` if *`rg`* is a range with the same iterator and sentinel type. -- [`common_view(views::all(rg))`](common-view-class.md) if *`rg`* has different iterator and sentinel types. +- `views::all(rg)` if `rg` is a range with the same iterator and sentinel type. +- [`common_view(views::all(rg))`](common-view-class.md) if `rg` has different iterator and sentinel types. ### Remarks -When an API requires the begin iterator and end sentinel have the same type, and the view that you're using doesn't meet that requirement, or you don't know if it does, use this range adaptor to create a `common_view`. It guarantees that the type of the begin iterator and sentinel are the same. +When an API requires the begin iterator and end sentinel to have the same type, and the view that you're using doesn't meet that requirement (or you don't know if it does), use this range adaptor to create a `common_view`. It guarantees that the type of the begin iterator and the type of the end sentinel are the same. ### Example: `common` @@ -213,7 +225,7 @@ int main() std::list lst{1, 2, 3, 4, 5, 6, 7, 8, 9}; auto firstFive = std::views::take(lst, 5); - // firstFive.begin(), firstFive.end() have different types: counted_iterator vs default_sentinel + // firstFive.begin(), firstFive.end() have different types: counted_iterator versus default_sentinel // auto r = std::accumulate(firstFive.begin(), firstFive.end(), 0); // Error: accumulate() requires firstFive.begin() and firstFive.end() types to be the same. auto common = std::views::common(firstFive); // create a common_view that has the same begin/end iterator types @@ -227,7 +239,7 @@ int main() ## `counted` - Create a view of the first *`count`* elements of a range, starting from the specified location. +Create a view of the first `count` elements of a range, starting from the specified location. ```cpp template @@ -236,29 +248,30 @@ constexpr auto counted(Iterator&& it, iter_difference_t count); ### Parameters -*`DifferenceType`*\ +`DifferenceType`\ The type of the count. -*`Iterator`*\ +`Iterator`\ The type of the iterator. -*`count`*\ +`count`\ The number of elements to include in the view. Must be non-negative. - If `count == 0`, an empty [`span`](span-class.md) is returned. -- If *`count`* is greater than the number of elements in the range, the behavior is undefined. +- If `count` is greater than the number of elements in the range, the behavior is undefined. -*`it`*\ -An iterator to the element in the range to start with. The element the iterator points to is included in the created view. +`it`\ +An iterator to the element in the range to start with. The element that the iterator points to is included in the created view. ### Return value -A [`span`](span-class.md) is returned if *`it`* is a `contiguous_iterator` for arrays, vectors, and other containers that store their elements contiguously. Otherwise, a [`subrange`](subrange-class.md) is returned. +A [`span`](span-class.md) is returned if `it` is a `contiguous_iterator` for arrays, vectors, and other containers that store their elements contiguously. Otherwise, a [`subrange`](subrange-class.md) is returned. ### Remarks -The included elements are `[it, count)`.\ -Once the view is created, the number of elements in the view stays the same even if the range it was created from changes. However, if the underlying range changes, accessing elements from the view may result in undefined behavior. +The included elements are `it` and `count`. + +After the view is created, the number of elements in the view stays the same, even if the range that it was created from changes. However, if the underlying range changes, accessing elements from the view might result in undefined behavior. ### Example: `counted` @@ -297,7 +310,7 @@ Hi ## `drop` -Create a view that excludes the first *N* elements of a range. +Create a view that excludes the first *n* elements of a range. ```cpp 1) template @@ -309,37 +322,38 @@ constexpr /* range closure object */ drop(DifferenceType&& count); ### Parameters -*`DifferenceType`*\ +`DifferenceType`\ The type that describes the number of elements to skip. -*`count`*\ -The number of elements to drop from the front of *`rg`*. Must be non-negative. -- If `count == 0`, all the elements in *`rg`* are returned. -- If *`count`* is greater than the number of elements in *`rg`*, then an empty view is returned. +`count`\ +The number of elements to drop from the front of `rg`. Must be non-negative. +- If `count == 0`, all the elements in `rg` are returned. +- If `count` is greater than the number of elements in `rg`, an empty view is returned. -*`R`*\ +`R`\ The type of the range. -*`rg`*\ -The range used to create the view. +`rg`\ +The range that's used to create the view. ### Return value -A view of the underlying range, with the specified number of elements dropped from the front.\ -If you specify more elements to drop than exist in the underlying range, then an [`empty_view`](empty-view-class.md) is returned. +A view of the underlying range, with the specified number of elements dropped from the front. + +If you specify more elements to drop than exist in the underlying range, an [`empty_view`](empty-view-class.md) is returned. The returned view is typically, but not always, a specialization of [`drop_view`](drop-view-class.md). That is: -- If `V` is a specialization of [`empty_view`](empty-view-class.md), or a specialization of [`span`](span-class.md), [`basic_string_view`](basic-string-view-class.md), [`iota_view`](iota-view-class.md) or [`subrange`](subrange-class.md) that is both `random_access_range` and `sized_range`, the result is a specialization of `V`. +- If `V` is a specialization of [`empty_view`](empty-view-class.md), or is a specialization of [`span`](span-class.md), [`basic_string_view`](basic-string-view-class.md), [`iota_view`](iota-view-class.md), or [`subrange`](subrange-class.md) that is both `random_access_range` and `sized_range`, the result is a specialization of `V`. - Otherwise, the result is a [`drop_view`](drop-view-class.md). ### Remarks -Once created, the number of elements in the view stays the same even if the view it was created from changes. However, if the underlying view changes, accessing elements in the returned view may result in undefined behavior. +After it's created, the number of elements in the view stays the same even if the view that it was created from changes. However, if the underlying view changes, accessing elements in the returned view might result in undefined behavior. `drop` is the opposite of [`take`](#take). -2\) Can be used with pipe syntax: `collection | drop(5)`. Or with function call syntax: `drop(collection, 5)` or `drop(5)(collection)`. +The code shown earlier as "2\)" can be used with pipe syntax: `collection | drop(5)`. Or it can be used with function call syntax: `drop(collection, 5)` or `drop(5)(collection)`. ### Example: `drop` @@ -375,7 +389,7 @@ int main() ## `drop_while` -Create a view that contains the elements of a range that remain once the leading elements that match the specified condition are dropped. +Create a view that contains the elements of a range that remain after the leading elements that match the specified condition are dropped. ```cpp 1) template @@ -387,25 +401,26 @@ constexpr /*range adaptor closure*/ drop_while(P&& predicate); ### Parameters -*`R`*\ +`R`\ The type of the range. -*`predicate`*\ +`predicate`\ The conditions that determine which leading elements to drop from the range. -*`rg`*\ +`rg`\ The underlying range to create the view from. ### Return value -A [`drop_while_view`](drop-while-view-class.md) consisting of the elements that remain when the leading elements that match the predicate are dropped. +A [`drop_while_view`](drop-while-view-class.md) that consists of the elements that remain when the leading elements that match the predicate are dropped. ### Remarks -Stops dropping elements from *`rg`* as soon as the predicate returns `false`.\ +Stops dropping elements from `rg` as soon as the predicate returns `false`. + `drop_while` is the opposite of [`take_while`](#take_while). -2\) Can be used with pipe syntax: `collection | drop_while(predicate)`. Or with function call syntax: `drop_while(collection, predicate)` or `drop_while(predicate)(collection)`. +The code shown earlier as "2\)" can be used with pipe syntax: `collection | drop_while(predicate)`. Or it can be used with function call syntax: `drop_while(collection, predicate)` or `drop_while(predicate)(collection)`. ### Example: `drop_while` @@ -454,18 +469,18 @@ constexpr ranges::view auto elements(R&& rg); ### Parameters -*`N`*\ +`N`\ The index of the element to select from each tuple-like value to include in the view. -*`R`*\ +`R`\ The type of the underlying range. -*`rg`*\ +`rg`\ The range of tuple-like values to create the view from. ### Return value -An [`elements_view`](elements-view-class.md) consisting of the selected index into each tuple-like value in a collection. +An [`elements_view`](elements-view-class.md) that consists of the selected index into each tuple-like value in a collection. ### Example: `elements` @@ -519,7 +534,7 @@ inline constexpr empty_view empty{}; ### Parameters -*`T`*\ +`T`\ The type of the elements in the view. The view needs an element type, even though there are no elements. ### Return value @@ -528,7 +543,7 @@ An [`empty_view`](empty-view-class.md). ### Remarks -An `empty_view` can be useful when calling code that requires a view, but doesn't need to process any of its elements. +An `empty_view` can be useful when you're calling code that requires a view but doesn't need to process any of its elements. ### Example: `empty` @@ -564,27 +579,27 @@ constexpr /*range adaptor closure*/ filter(P&& predicate); ### Parameters -*`P`*\ +`P`\ The type of the predicate. -*`predicate`*\ -The condition(s) that determine which elements to keep in the range. +`predicate`\ +The conditions that determine which elements to keep in the range. -*`R`*\ +`R`\ The type of the underlying range. -*`rg`*\ +`rg`\ The range to create the view from. ### Return value -A [`filter_view`](filter-view-class.md) containing the elements of a range that match the predicate. +A [`filter_view`](filter-view-class.md) that contains the elements of a range that match the predicate. ### Remarks For efficiency's sake, when you use `filter` and `transform` together with a pipe `|`, do the `filter` first so that you `transform` only the elements that you intend to keep. -2\) Can be used with pipe syntax: `collection | filter(predicate)`. Or with function call syntax: `filter(collection, predicate)` or `filter(predicate)(collection)`. +The code shown earlier as "2\)" can be used with pipe syntax: `collection | filter(predicate)`. Or it can be used with function call syntax: `filter(collection, predicate)` or `filter(predicate)(collection)`. ### Example: `filter` @@ -633,17 +648,17 @@ constexpr ranges::view auto iota(V&& startValue, E&& endValue); // create a boun ### Parameters -*`E`*\ +`E`\ The type of the end value. -*`S`*\ +`S`\ The type of the start value. -*`startValue`*\ +`startValue`\ The first value in the sequence. -*`endValue`*\ -This value is one past the last value that will be in the sequence. For example, `std::views::iota(0, 5)` generates a view that has the values 0,1,2,3,4. +`endValue`\ +This value is one past the last value that will be in the sequence. For example, `std::views::iota(0, 5)` generates a view that has the values `0,1,2,3,4`. ### Return value @@ -696,16 +711,18 @@ views::istream(str); ### Parameters -*`str`*\ -A stream object. The type of which is derived from a specialization of `std::basic_istream`. +`str`\ +A stream object. Its type is derived from a specialization of `std::basic_istream`. -*`Val`*\ +`Val`\ The type of the elements to extract from the stream. ### Return value -A [`basic_istream_view`](basic-istream-view-class.md).\ -This range adaptor is equivalent to `ranges::basic_istream_view(str)` where `U` is the type of `str`. +A [`basic_istream_view`](basic-istream-view-class.md). + + +This range adaptor is equivalent to `ranges::basic_istream_view(str)`, where `U` is the type of `str`. ### Example: `istream` @@ -732,7 +749,7 @@ int main() ## `join` -Create a view that combines all of the elements of multiple ranges into a single view. +Create a view that combines all the elements of multiple ranges into a single view. ```cpp 1) template @@ -743,15 +760,15 @@ Create a view that combines all of the elements of multiple ranges into a single ### Parameters -*`R`*\ +`R`\ The type of the underlying range. -*`rg`*\ +`rg`\ The range to create the view from. ### Return value -A [`join_view`](join-view-class.md) that contains the elements of all of the ranges in the underlying range. +A [`join_view`](join-view-class.md) that contains the elements of all the ranges in the underlying range. ### Example: `join` @@ -779,11 +796,11 @@ C++ 20 contains: ranges modules concepts & more. ### Remarks -2\) Can be used with pipe syntax: `collection | join`. Or with function call syntax: `join(collection)`. +The code shown earlier as "2\)" can be used with pipe syntax: `collection | join`. Or it can be used with function call syntax: `join(collection)`. ## `keys` -Create a [`keys_view`](keys-view-class.md) of the first index into each tuple-like value in a collection. This is useful for extracting keys from associative containers. For example, given a range of `std::tuple`, create a view consisting of all the `string` elements from each tuple. +Create a [`keys_view`](keys-view-class.md) of the first index into each tuple-like value in a collection. This is useful for extracting keys from associative containers. For example, given a range of `std::tuple`, create a view that consists of all the `string` elements from each tuple. ```cpp template @@ -792,12 +809,12 @@ constexpr auto keys(R&& rg); ### Parameters -*`R`*\ +`R`\ The type of the underlying range. ### Return value -A [`keys_view`](keys-view-class.md) consisting of the first index into each tuple-like value in the range. +A [`keys_view`](keys-view-class.md) that consists of the first index into each tuple-like value in the range. ### Example: `keys` @@ -874,36 +891,36 @@ constexpr /*range adaptor closure*/ lazy_split(Pattern&& delimiter); ### Parameters -*`delimiter`*\ +`delimiter`\ A single value, or a sequence of values that specify where to split the range. -*`Pattern`*\ +`Pattern`\ The type of the delimiter. -*`R`*\ +`R`\ The type of the range to split. -*`rg`*\ +`rg`\ The range to split. ### Return value -A [`lazy_split_view`](lazy-split-view-class.md) containing one or more subranges that is the result of splitting the original range on *`delimiter`*. +A [`lazy_split_view`](lazy-split-view-class.md) that contains one or more subranges and is the result of splitting the original range on `delimiter`. ### Remarks -The delimiter isn't part of the result. For example, if you split the range 1,2,3 on the value 2, you get two subranges: 1 and 3. +The delimiter isn't part of the result. For example, if you split the range `1,2,3` on the value `2`, you get two subranges: `1` and `3`. A related adaptor is [`split`](#split). The primary differences between `split_view` and `lazy_split_view` are: -| **View** | **Can split a `const` range** | **range iterator** | -|--|--| -| `split_view` | no | Supports `forward_range` or higher. | -| `lazy_split_view` | yes | `input_range` or higher. | +| View | Can split a `const` range | Range iterator | +|--|--|--| +| `split_view` | no | Supports `forward_range` or higher | +| `lazy_split_view` | yes | `input_range` or higher | -Prefer `split_view` because it's more efficient unless you must split a range that is `const`. +Prefer `split_view` because it's more efficient, unless you must split a range that is `const`. -2\) Can be used with pipe syntax: `collection | lazy_split(delimiter)`. Or with function call syntax: `lazy_split(collection, delimiter)` or `lazy_split(delimiter)(collection)`. +The code shown earlier as "2\)" can be used with pipe syntax: `collection | lazy_split(delimiter)`. Or it can be used with function call syntax: `lazy_split(collection, delimiter)` or `lazy_split(delimiter)(collection)`. ### Example: `lazy_split` @@ -964,10 +981,10 @@ constexpr ranges::view auto reverse(R&& rg); ### Parameters -*`R`*\ +`R`\ The type of the underlying range to reverse. -*`rg`*\ +`rg`\ The range to reverse. ### Return value @@ -980,7 +997,7 @@ A view that presents the elements of the underlying range in reverse order. The ### Remarks -2\) Can be used with pipe syntax: `collection | reverse`. Or with function call syntax: `reverse(collection)`. +The code shown earlier as "2\)" can be used with pipe syntax: `collection | reverse`. Or it can be used with function call syntax: `reverse(collection)`. ### Example: `reverse` @@ -1017,7 +1034,7 @@ int main() ## `single` -Create a `single_view`, which is a view containing one element. +Create a `single_view`, which is a view that contains one element. ```cpp template @@ -1026,19 +1043,19 @@ constexpr ranges::view auto single(T&& t); ### Parameters -*`T`*\ +`T`\ The type of the element in the view. -*`t`*\ +`t`\ The value of the element to store in the view. ### Return value -An [`single_view`](single-view-class.md) containing *`t`*. +An [`single_view`](single-view-class.md) that contains `t`. ### Remarks -This view is useful for test purposes for calling code that needs to be provided with a view with at least one element in it. +This view is useful for test purposes, for calling code that needs to be provided with a view that has at least one element in it. ### Example: `single` @@ -1078,36 +1095,36 @@ constexpr /*range adaptor closure*/ split(Pattern&& delimiter); ### Parameters -*`delimiter`*\ +`delimiter`\ A single value, or a sequence of values that specify where to split the range. -*`Pattern`*\ +`Pattern`\ The type of the delimiter. -*`R`*\ +`R`\ The type of the underlying range to split. -*`rg`*\ +`rg`\ The range to split. ### Return value -A [`split_view`](split-view-class.md) containing one or more subranges. +A [`split_view`](split-view-class.md) that contains one or more subranges. ### Remarks -The delimiter isn't part of the result. For example, if you split the range 1,2,3 on the value 2, you get two subranges: 1 and 3. +The delimiter isn't part of the result. For example, if you split the range `1,2,3` on the value `2`, you get two subranges: `1` and `3`. A related adaptor is [`lazy_split`](#lazy_split). The primary differences between `split_view` and `lazy_split_view` are: -| **view** | **Can split a `const` range** | **range type** | -|---|---| -| `split_view` | no | Supports `forward_range` or higher. | -| `lazy_split_view` | yes | Supports `input_range` or higher. | +| View | Can split a `const` range | Range type | +|---|---|---| +| `split_view` | no | Supports `forward_range` or higher | +| `lazy_split_view` | yes | Supports `input_range` or higher | -Prefer `split_view` because it's more efficient. Unless you must split a range that is `const`. +Prefer `split_view` because it's more efficient, unless you must split a range that is `const`. -2\) Can be used with pipe syntax: `collection | split(delimiter)`. Or with function call syntax: `split(collection, 5)` or `split(5)(collection)`. +The code shown earlier as "2\)" can be used with pipe syntax: `collection | split(delimiter)`. Or it can be used with function call syntax: `split(collection, 5)` or `split(5)(collection)`. ### Example: `split` @@ -1168,29 +1185,29 @@ constexpr /*range adaptor closure*/ take(DifferenceType&& count); ### Parameters -*`R`*\ +`R`\ The type of the underlying range. -*`rg`*\ +`rg`\ The range to create the view from. -*`count`*\ -The number of elements to take from the front of *`rg`*. +`count`\ +The number of elements to take from the front of `rg`. ### Return value The returned view is typically, but not always, a specialization of [`take_view`](take-view-class.md). Specifically: -- If `V` is a specialization of [`empty_view`](empty-view-class.md), or a specialization of [`span`](span-class.md), [`basic_string_view`](basic-string-view-class.md), [`iota_view`](iota-view-class.md) or [`subrange`](subrange-class.md) that is both `random_access_range` and `sized_range`, the result is a specialization of `V`. +- If `V` is a specialization of [`empty_view`](empty-view-class.md), or is a specialization of [`span`](span-class.md), [`basic_string_view`](basic-string-view-class.md), [`iota_view`](iota-view-class.md), or [`subrange`](subrange-class.md) that is both `random_access_range` and `sized_range`, the result is a specialization of `V`. - Otherwise, the result is a [`take_view`](take-view-class.md). ### Remarks -If you specify more elements to take than exist in *`rg`*, then all of the elements are taken. +If you specify more elements to take than exist in `rg`, all of the elements are taken. `take` is the opposite of [`drop`](#drop). -2\) Can be used with pipe syntax: `collection | take(5)`. Or with function call syntax: `take(5, collection)` or `take(5)(collection)`. +The code shown earlier as "2\)" can be used with pipe syntax: `collection | take(5)`. Or it can be used with function call syntax: `take(5, collection)` or `take(5)(collection)`. ### Example: `take` @@ -1238,29 +1255,29 @@ constexpr /*range adaptor closure*/ take_while(P&& predicate); ### Parameters -*`P`*\ +`P`\ The type of the predicate. -*`predicate`*\ +`predicate`\ The conditions that determine which leading elements to copy from the range. -*`R`*\ +`R`\ The type of the underlying range. -*`rg`*\ +`rg`\ The range to create the view from. ### Return value -A [`take_while_view`](take-while-view-class.md) consisting of the first *`count`* elements that meet the specified criteria in the range. +A [`take_while_view`](take-while-view-class.md) that consists of the first `count` elements that meet the specified criteria in the range. ### Remarks -Stops taking elements from *`rg`* once the predicate returns `false` or the range runs out of elements. +Stops taking elements from `rg` after the predicate returns `false` or the range runs out of elements. `take_while` is the opposite of [`drop_while`](#drop_while). -2\) Can be used with pipe syntax: `collection | take_while(pred)`. Or with function call syntax: `take_while(collection, pred)` or `take_while(pred)(collection)`. +The code shown earlier as "2\)" can be used with pipe syntax: `collection | take_while(pred)`. Or it can be used with function call syntax: `take_while(collection, pred)` or `take_while(pred)(collection)`. ### Example: `take_while` @@ -1310,27 +1327,27 @@ constexpr /*range adaptor closure*/ transform(F&& fun); ### Parameters -*`F`*\ +`F`\ The type of the function object to transform the elements. -*`R`*\ +`R`\ The type of the underlying range. -*`fun`*\ +`fun`\ The function that transforms the elements. -*`rg`*\ +`rg`\ The range to create the view from. ### Return value -A [`transform_view`](transform-view-class.md) containing the transformed elements of *`rg`*. +A [`transform_view`](transform-view-class.md) that contains the transformed elements of `rg`. ### Remarks For efficiency's sake, when you compose `filter` and `transform`, do the `filter` first so that you `transform` only the elements that you intend to keep. -2\) Can be used with pipe syntax: `collection | transform(fun)`. Or with function call syntax: `transform(collection, fun)` or `transform(fun)(collection)`. +The code shown earlier as "2\)" can be used with pipe syntax: `collection | transform(fun)`. Or it can be used with function call syntax: `transform(collection, fun)` or `transform(fun)(collection)`. ### Example: `transform` @@ -1367,7 +1384,7 @@ int main() ## `values` -Create a [`values_view`](values-view-class.md) consisting of the second index into each tuple-like value in a collection. This is useful for making a view of the values in an associative container. For example, given a range of `std::tuple` values, create a view consisting of all the `int` elements from each tuple. +Create a [`values_view`](values-view-class.md) that consists of the second index into each tuple-like value in a collection. This is useful for making a view of the values in an associative container. For example, given a range of `std::tuple` values, create a view that consists of all the `int` elements from each tuple. ```cpp template @@ -1376,10 +1393,10 @@ constexpr ranges::view auto values(R&& rg); ### Parameters -*`R`*\ +`R`\ The type of the underlying range. -*`rg`*\ +`rg`\ The underlying range of tuple-like values. ### Return value @@ -1434,8 +1451,8 @@ int main() {"Windows 2000", 2000} }; - // Another way to call the range adaptor using '|' - // Create a values_view containing the year from each pair + // Another way to call the range adaptor by using '|' + // Create a values_view that contains the year from each pair for (int years : windows | std::views::values) { std::cout << years << ' '; // 1985 1987 1990 1992 ... @@ -1461,12 +1478,12 @@ using all_t = decltype(views::all(std::declval())); ### Parameters -*`R`*\ +`R`\ The type of the underlying range. ### Return value -The type of the view that `all` returns: `decltype(views::all(std::declval()))` +The type of the view that `all` returns: `decltype(views::all(std::declval()))`. ### Example: `all_t` @@ -1486,4 +1503,4 @@ int main() ## See also [``](ranges.md)\ -[view classes](view-classes.md) \ No newline at end of file +[View classes](view-classes.md) \ No newline at end of file diff --git a/docs/standard-library/ranges.md b/docs/standard-library/ranges.md index eac9034cbd6..ed4390f9103 100644 --- a/docs/standard-library/ranges.md +++ b/docs/standard-library/ranges.md @@ -1,6 +1,6 @@ --- title: "" -description: "Overview of the Standard Template Library (STL) ranges library" +description: "Get an overview of the Standard Template Library (STL) ranges." ms.date: 10/25/2022 f1_keywords: [""] helpviewer_keywords: ["ranges"] @@ -8,17 +8,17 @@ helpviewer_keywords: ["ranges"] # `` -At a high level, a range is something you can iterate over. The containers, such as `vector`, `list`, and so on, in the C++ Standard Library are ranges. A range abstracts iterators in a way that simplifies and amplifies your ability to use the Standard Template Library (STL). +At a high level, a *range* is something that you can iterate over. The containers, such as `vector` and `list`, in the C++ Standard Library are ranges. A range abstracts iterators in a way that simplifies and amplifies your ability to use the Standard Template Library (STL). -STL algorithms usually take iterators that point to the portion of the collection they should operate on. For example, consider how you sort a `vector` using `std::sort()`. You pass two iterators the mark the beginning and end of the `vector`. That provides flexibility, but passing the iterators to the algorithm is extra work since most of the time you just want to sort the whole thing. +STL algorithms usually take iterators that point to the portion of the collection that they should operate on. For example, consider how you sort a `vector` by using `std::sort()`. You pass two iterators that mark the beginning and end of the `vector`. That provides flexibility, but passing the iterators to the algorithm is extra work because you probably just want to sort the whole thing. -With ranges, you can call `std::ranges::sort(myVector);` which is treated as if you had called `std::sort(myVector.begin(), myVector.end());` In range libraries, algorithms take ranges as parameters (although they can also take iterators, if you want). They can operate directly on collections. Some examples of range algorithms available in `` 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`. +With ranges, you can call `std::ranges::sort(myVector);`, which is treated as if you called `std::sort(myVector.begin(), myVector.end());`. In range libraries, algorithms take ranges as parameters (although they can also take iterators, if you want). They can operate directly on collections. Examples of range algorithms available in `` include `copy`, `copy_n`, `copy_if`, `all_of`, `any_of`, `none_of`, `find`, `find_if`, `find_if_not`, `count`, `count_if`, `for_each`, `for_each_n`, `equal`, and `mismatch`. -But the benefits of ranges go further. Perhaps the most important benefit is that you can compose STL algorithms that operate on ranges in a style reminiscent of functional programming. +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. -## A ranges example +## An example of ranges -Before ranges, if you wanted to transform the elements of a collection that meet a certain criteria, you'd need 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 3, you could write something like: +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: ```cpp std::vector input = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; @@ -37,26 +37,26 @@ std::vector input = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; auto output = input | std::views::filter([](const int n) {return n % 3 == 0; }) | std::views::transform([](const int n) {return n * n; }); ``` -Besides being easier to read, it avoids the memory allocation required for the `intermediate` vector and its contents, while also allowing you to compose two operations. +Besides being easier to read, this code avoids the memory allocation that's required for the `intermediate` vector and its contents. It also allows you to compose two operations. -In the code above, each element that is divisible by three is combined with an operation to square that element. The '`|`' symbol chains the operations together, and is read left to right. +In the preceding code, each element that's divisible by three is combined with an operation to square that element. The pipe (`|`) symbol chains the operations together and is read left to right. The result, `output`, is itself a kind of range called a *view*. > [!NOTE] -> The ranges examples require the [`/std:c++latest`](../build/reference/std-specify-language-standard-version.md) compiler option. Because post-release updates to `` in the C++20 Standard are a work in progress, the features that require `std::views` aren't enabled yet under **`/std:c++20`**. +> This example of ranges requires the [`/std:c++latest`](../build/reference/std-specify-language-standard-version.md) compiler option. Because post-release updates to `` in the C++20 standard are a work in progress, the features that require `std::views` aren't enabled yet under `/std:c++20`. ## Views -A *view* is a lightweight range. View operations such as default construction, move construction/assignment, copy construction/assignment (if present), destruction, begin and end, all happen in constant time regardless of the number of elements in the view. +A view is a lightweight range. View operations--such as default construction, move construction/assignment, copy construction/assignment (if present), destruction, begin, and end--all happen in constant time regardless of the number of elements in the view. Views are created by range adaptors, which are discussed in the following section. For more information about the classes that implement various views, see [View classes](view-classes.md). -How the elements in the view appear depends on the range adaptor you use to create the view. In the previous example, a range adaptor takes a range and returns a view of the elements divisible by three. The underlying range is unchanged. +How the elements in the view appear depends on the range adaptor that you use to create the view. In the previous example, a range adaptor takes a range and returns a view of the elements divisible by three. The underlying range is unchanged. Views are composable, which is powerful. In the previous example, the view of vector elements that are divisible by three is combined with the view that squares those elements. -The elements of a view are evaluated lazily. That is, the transformations you apply to each element in a view aren't evaluated until you ask for the 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 `divisible_by_three` lambda breakpoint as each element in `input` is tested for divisibility by three. The `square` lambda breakpoint will be hit as the elements that are divisible by three are squared. +The elements of a view are evaluated lazily. That is, the transformations that you apply to each element in a view aren't evaluated until you ask for the 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 `divisible_by_three` lambda breakpoint as each element in `input` is tested for divisibility by three. The `square` lambda breakpoint will be hit as the elements that are divisible by three are squared. ```cpp // requires /std:c++latest @@ -85,21 +85,21 @@ int main() Range adaptors take a range and produce a view. Range adaptors produce lazily evaluated views. That is, you don't incur the cost of transforming every element in the range to produce the view. You only pay the cost to process an element in the view when you access that element. -In the previous example, the `filter` range adaptor creates a view named `input` containing the elements that are divisible by three. The `transform` range adaptor takes the view of elements divisible by three, and creates a view of those elements squared. +In the previous example, the `filter` range adaptor creates a view named `input` that contains the elements that are divisible by three. The `transform` range adaptor takes the view of elements divisible by three and creates a view of those elements squared. -Range adaptors can be chained together (composed), which is the heart of the power and flexibility of ranges. Composing range adaptors allows you to overcome a core problem with the previous STL algorithms because they aren't easily composable. +Range adaptors can be chained together (composed), which is the heart of the power and flexibility of ranges. Composing range adaptors allows you to overcome the problem that the previous STL algorithms aren't easily composable. For more information about creating views, see [Range adaptors](range-adaptors.md). ## Range algorithms -Range algorithms have been created that take a range argument. For example, `std::ranges::sort(myVector);` +Some range algorithms take a range argument. An example is `std::ranges::sort(myVector);`. -The range algorithms are almost identical to the corresponding iterator-pair algorithms in the `std` namespace, except 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. +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. ## `` functions -| **Get an iterator to a range** | **Description** | +| Function | Description | |--|--| | [`begin`](range-functions.md#begin)C++20 | Get an iterator to the first element in the range. | | [`cbegin`](range-functions.md#cbegin)C++20 | Get a `const` iterator to the first element in the range. | @@ -119,20 +119,20 @@ The range algorithms are almost identical to the corresponding iterator-pair alg How you view the elements of a range depends on its underlying iterator type. The iterator types are specified as C++20 concepts. -In C++ 20, to say that concept X refines concept Y means that everything that satisfies concept Y also satisfies concept X. For example: car, bus, and truck all refine vehicle. +In C++20, to say that concept *X* refines concept *Y* means that everything that satisfies concept *Y* also satisfies concept *X*. For example: *car*, *bus*, and *truck* all refine *vehicle*. -The range concepts mirror the hierarchy of iterator categories. The following table lists various range concepts, along with the type of container they can be applied to: +The range concepts mirror the hierarchy of iterator categories. The following table lists various range concepts, along with the types of containers that they can be applied to: | Range concept | Description | Supported containers | |--|--|--| -| `std::ranges::input_range` | Can iterate from beginning to end at least once | `std::forward_list`
`std::unordered_map`
`std::unordered_multimap`
`std::unordered_set`
`std::unordered_multiset`
`basic_istream_view` | -| `std::ranges::forward_range` | Can iterate from beginning to end more than once) | `std::forward_list`
`std::unordered_map`
`std::unordered_multimap`
`std::unordered_set`
`std::unordered_multiset` | -| `std::ranges::bidirectional_range` | Can iterate forward and backward more than once | `std::list`
`std::map`
`std::multimap`
`std::multiset`
`std::set`| -| `std::ranges::random_access_range` | Can access an arbitrary element (in constant time) using the `[]` operator) | `std::deque` | -| `std::ranges::contiguous_range` | The elements are stored in memory consecutively | `std::array`
`std::string`
`std::vector` | +| `std::ranges::input_range` | Can iterate from beginning to end at least once. | `std::forward_list`
`std::unordered_map`
`std::unordered_multimap`
`std::unordered_set`
`std::unordered_multiset`
`basic_istream_view` | +| `std::ranges::forward_range` | Can iterate from beginning to end more than once. | `std::forward_list`
`std::unordered_map`
`std::unordered_multimap`
`std::unordered_set`
`std::unordered_multiset` | +| `std::ranges::bidirectional_range` | Can iterate forward and backward more than once. | `std::list`
`std::map`
`std::multimap`
`std::multiset`
`std::set`| +| `std::ranges::random_access_range` | Can access an arbitrary element (in constant time) by using the `[]` operator. | `std::deque` | +| `std::ranges::contiguous_range` | The elements are stored in memory consecutively. | `std::array`
`std::string`
`std::vector` | ## See also [Range functions](range-functions.md)\ [Range adaptors](range-adaptors.md)\ -[Header Files Reference](../standard-library/cpp-standard-library-header-files.md) \ No newline at end of file +[Header files reference](../standard-library/cpp-standard-library-header-files.md) \ No newline at end of file diff --git a/docs/standard-library/view-classes.md b/docs/standard-library/view-classes.md index a78c1052b03..513e1cbe4ce 100644 --- a/docs/standard-library/view-classes.md +++ b/docs/standard-library/view-classes.md @@ -1,5 +1,5 @@ --- -description: "Learn more about view classes, which allow you inexpensively refer to and transform ranges." +description: "Learn more about view classes, which allow you to inexpensively refer to and transform ranges." title: "View classes" ms.date: 10/07/2022 f1_keywords: ["RANGES/std::ranges::views", "RANGES/std::views"] @@ -7,15 +7,17 @@ helpviewer_keywords: ["RANGES/VIEWS/std", "VIEWS/std"] --- # View classes -A *view* is a lightweight range that refers to elements that it doesn't own, (except for [`owning_view`](owning-view-class.md)). A view is typically based on another range and provides a different way of looking at it, whether by transforming or filtering it. For example, [`std::views::filter`](filter-view-class.md) is a view that uses the criteria you specify to select elements from another range. +A *view* is a lightweight range that refers to elements that it doesn't own (except for [`owning_view`](owning-view-class.md)). A view is typically based on another range and provides a different way of looking at it, whether by transforming or filtering it. For example, [`std::views::filter`](filter-view-class.md) is a view that uses the criteria that you specify to select elements from another range. -When you access the elements in a view, it's done 'lazily' so that work is only done when you get an element. This also makes it possible to combine, or 'compose' views without a performance penalty. For example, you could create a view that provides only the even elements from a range and then transform them by squaring them. The work to do the filtering and transformation is done only for the elements you access, and only when you access them. +When you access the elements in a view, it's done "lazily" so that work is done only when you get an element. This also makes it possible to combine, or *compose*, views without a performance penalty. -A view can be copied, assigned, and destroyed in constant time no matter how many elements it contains. This is because a view doesn't own the elements it refers to, so it doesn't need to make a copy. This is also why you can compose views without a performance penalty. +For example, you could create a view that provides only the even elements from a range and then transform them by squaring them. The work to do the filtering and transformation is done only for the elements that you access, and only when you access them. -You typically create a view using a [range adaptor](range-adaptors.md). Range adaptors are the intended way to create a view, are easier to use than instantiating the view classes directly, and are sometimes more efficient than instantiating the view classes directly. The view classes are exposed directly primarily in case you need to create your own custom view type based on an existing view type. +A view can be copied, assigned, and destroyed in constant time no matter how many elements it contains. This is because a view doesn't own the elements that it refers to, so it doesn't need to make a copy. This is also why you can compose views without a performance penalty. -Here's a brief example of creating a view of the squares of the elements that are divisible by 3 in a vector: +You typically create a view by using a [range adaptor](range-adaptors.md). Range adaptors are the intended way to create a view, are easier to use than instantiating the view classes directly, and are sometimes more efficient than instantiating the view classes directly. The view classes are exposed directly in case you need to create your own custom view type based on an existing view type. + +Here's a brief example of creating a view of the squares of the elements that are divisible by three in a vector: ```cpp // requires /std:c++20 or later @@ -43,9 +45,9 @@ int main() 0 9 36 81 ``` -Using a view after the range it's based on is modified can lead to undefined behavior. For example, a [`reverse_view`](reverse-view-class.md) based on a vector shouldn't be reused if you add/remove elements from the underlying vector. Modifying the underlying vector invalidates the container's `end` iterator--including the copy of the iterator that the view may have made. +Using a view after the range that it's based on is modified can lead to undefined behavior. For example, a [`reverse_view`](reverse-view-class.md) based on a vector shouldn't be reused if you add or remove elements from the underlying vector. Modifying the underlying vector invalidates the container's `end` iterator--including the copy of the iterator that the view might have made. -Because views are cheap to create, you should generally recreate a view if you modify the underlying range. The following example demonstrates this, and also shows how to store a view pipeline in a variable so that you can reuse it: +Because views are cheap to create, you should generally re-create a view if you modify the underlying range. The following example demonstrates this. It also shows how to store a view pipeline in a variable so that you can reuse it. ```cpp // requires / std:c++20 or later @@ -96,65 +98,71 @@ v | rev3(v): 2 1 0 The following view classes are defined in the `std::ranges` namespace. -| **View ** | **Description** | +| View | Description | |--|--| | [`basic_istream_view`](basic-istream-view-class.md)C++20 | A view of successive elements from an input stream. | | [`common_view`](common-view-class.md)C++20 | Adapts a view that has different iterator/sentinel types into a view with the same iterator/sentinel types. | | [`drop_view`](drop-view-class.md)C++20 | Created from another view, skipping the first `count` elements. | | [`drop_while_view`](drop-while-view-class.md)C++20 | Created from another view, skipping leading elements as long as a predicate holds. | -| [`elements_view`](elements-view-class.md)C++20 | A view over the selected index into each tuple-like value in a collection. For example, given a range of `std::tuple` values, create a view consisting of all the `string` elements from each tuple. | +| [`elements_view`](elements-view-class.md)C++20 | A view over the selected index into each tuple-like value in a collection. For example, given a range of `std::tuple` values, create a view that consists of all the `string` elements from each tuple. | | [`empty_view`](empty-view-class.md)C++20 | A view with no elements. | | [`filter_view`](filter-view-class.md)C++20 | Filters out elements of a range that don't match a predicate. | | [`iota_view`](iota-view-class.md)C++20 | A generated view that contains a sequence of incrementing values. | -| [`join_view`](join-view-class.md)C++20 | Combines all of the elements of multiple ranges into a single view. | -| [`keys_view`](keys-view-class.md)C++20 | A view over the first index into each tuple-like value in a collection. For example, given a range of `std::tuple` values, create a view consisting of the `string` elements from each tuple. | +| [`join_view`](join-view-class.md)C++20 | Combines all the elements of multiple ranges into a single view. | +| [`keys_view`](keys-view-class.md)C++20 | A view over the first index into each tuple-like value in a collection. For example, given a range of `std::tuple` values, create a view that consists of the `string` elements from each tuple. | | [`lazy_split_view`](lazy-split-view-class.md)C++20 | Splits a view into subranges based on a delimiter. | | [`owning_view`](owning-view-class.md)C++20 | Takes ownership of the elements from another range. | | [`ref_view`](ref-view-class.md)C++20 | A view that references the elements that belong to another range. | | [`reverse_view`](reverse-view-class.md)C++20 | Presents the elements of a range in reverse order. | -| [`single_view`](single-view-class.md)C++20 | A view containing only one element. | +| [`single_view`](single-view-class.md)C++20 | A view that contains only one element. | | [`split_view`](split-view-class.md)C++20 | Splits a view into subranges based on a delimiter. | -| [`subrange`](subrange-class.md)C++20 | A view of part of the elements of a range as defined by a begin iterator and sentinel. | +| [`subrange`](subrange-class.md)C++20 | A view of part of the elements of a range, as defined by a begin iterator and a sentinel. | | [`take_view`](take-view-class.md)C++20 | Contains the specified number of elements taken from the front of a range. | | [`take_while_view`](take-while-view-class.md)C++20 | Contains the leading elements of a range that match the given predicate. | -| [`transform_view`](transform-view-class.md)C++20 | A view of an underlying sequence after applying a transformation function to each element. | -| [`values_view`](values-view-class.md)C++20 | A view over the second index into each tuple-like value in a collection. For example, given a range of `std::tuple` values, create a view consisting of the `int` elements from each tuple. | +| [`transform_view`](transform-view-class.md)C++20 | A view of an underlying sequence after a transformation function is applied to each element. | +| [`values_view`](values-view-class.md)C++20 | A view over the second index into each tuple-like value in a collection. For example, given a range of `std::tuple` values, create a view that consists of the `int` elements from each tuple. | -Many of these classes have corresponding [`range-adaptors`](range-adaptors.md) in the `std:views` namespace that creates them. Prefer the adaptors in `std::views` to creating view classes directly. The range adaptors are the intended access points, are easier to use, and in some cases are more efficient. +Many of these classes have corresponding [range adaptors](range-adaptors.md) in the `std:views` namespace that creates them. Prefer the adaptors in `std::views` to creating view classes directly. The range adaptors are the intended access points, are easier to use, and in some cases are more efficient. ## View classes characteristics -Each view class topic has a *Characteristics* section following the syntax section. The *Characteristics* section has the following entries: +Each view class topic has a **Characteristics** section after the syntax section. The **Characteristics** section has the following entries: + +* **Range adaptor**: A link to the range adaptor that creates the view. You typically use a range adaptor to create a view rather than create a view class directly, so it's listed here for convenience. +* **Underlying range**: Views have different iterator requirements for the kind of underlying range that they can use. See the following table for the hierarchy of iterators. +* **View iterator category**: The iterator category of the view. When a view adapts a range, the iterator type for the view is typically the same as the iterator type of the underlying range. However, it might be different for some views. For example, `reverse_view` has a `bidirectional_iterator` category, even if the underlying range has a `random_access_iterator` category. +* **Element type**: The type of the elements that the view's iterator returns. +* **Sized**: Whether the view can return the number of elements that it refers to. Not all views can. +* **Common range**: Specifies whether the view is a `common_range`, which means that the begin iterator and sentinel types are the same. Common ranges are useful for pre-range code that works with iterator pairs. An example is iterator pair constructors for a sequence container, like `vector(ranges::begin(x), ranges::end(x))`. +* **Borrowed range**: Specifies whether the view is a borrowed range. `borrowed_range` means you can use iterators for `T` after `T` is destroyed. + + No standard container is a borrowed range, because destroying the container frees the elements and invalidates any iterators. In that case, we say that the iterators are left "dangling" after destruction. -* **Range adaptor:** A link to the range adaptor that creates the view. You typically use a range adaptor to create a view rather than create a view class directly, so it's listed here for convenience. -* **Underlying range:** Views have different iterator requirements for the kind of underlying range they can use. See the table below for the hierarchy of iterators. -* **View iterator category:** The iterator category of the view. When a view adapts a range, the iterator type for the view is typically the same as the iterator type of the underlying range. However, it may be different for some views. For example, a `reverse_view` has a `bidirectional_iterator` category, even if the underlying range has a `random_access_iterator` category. -* **Element type:** The type of the elements that are returned by the view's iterator. -* **Sized:** Whether the view can return the number of elements it refers to. Not all views can. -* **Common range:** Specifies whether the view is a `common_range`, which means that the begin iterator and sentinel types are the same. Common ranges are useful for pre-ranges code that works with iterator pairs. For example, sequence container iterator pair constructors like `vector(ranges::begin(x), ranges::end(x))`. -* **Borrowed range:** Specifies whether the view is a *borrowed range*. `borrowed_range` means you can use iterators for `T` after `T` is destroyed. No standard container is a borrowed range because destroying the container frees the elements, so any iterators are invalidated. In that case, we say that the iterators are left "dangling" after destruction. For example,`std::ranges::find()` typically returns an iterator to the found element in the range argument. If the range argument is a temporary (rvalue) container, it's a mistake to store the returned iterator and use it later because it's "dangling". Range algorithms that return iterators (or subranges) only do so when their arguments are lvalues (non-temporaries) or borrowed ranges. Otherwise, they return a `std::dangling` object, which provides a hint in error messages about what has gone wrong if you tried to use it like an iterator. -* **Is `const` iterable**: Indicates whether you can iterate over a `const` instance of the view. Not all `const` views can be iterated. If a view isn't `const`-iterable, you can't iterate with `for (const auto& element : as_const(theView))` or pass it to a function that takes a const reference to the view and then tries to iterate over it. + For example, `std::ranges::find()` typically returns an iterator to the found element in the range argument. If the range argument is a temporary (rvalue) container, it's a mistake to store the returned iterator and use it later because it's "dangling." + + Range algorithms that return iterators (or subranges) do so only when their arguments are lvalues (non-temporaries) or borrowed ranges. Otherwise, they return a `std::dangling` object, which provides a hint in error messages about what went wrong if you tried to use it like an iterator. +* **Is `const` iterable**: Indicates whether you can iterate over a `const` instance of the view. Not all `const` views can be iterated. If a view isn't `const` iterable, you can't iterate with `for (const auto& element : as_const(theView))` or pass it to a function that takes a `const` reference to the view and then tries to iterate over it. ### Iterator hierarchy -The iterator category referred to in the *Characteristics* section by **Range adaptor** and **View iterator category** refers to the hierarchy of iterators that ranges and views support. +The iterator category that **Range adaptor** and **View iterator category** mention in the **Characteristics** section refers to the hierarchy of iterators that ranges and views support. That hierarchy, in increasing order of capability, is: -| **range iterator category** | **Description** | +| Range iterator category | Description | |--|--| -| `output_range` | Write-only, only moves forward; single-pass | -| `input_range` | Only moves forward; single-pass | -| `forward_range` | Only moves forward; multi-pass | +| `output_range` | Write-only, only moves forward; single-pass. | +| `input_range` | Only moves forward; single-pass. | +| `forward_range` | Only moves forward; multi-pass. | | `bidirectional_range` | Can move forward and backward; multi-pass. | | `random_access_range` | Can access the collection with an index; multi-pass. | | `contiguous_range` | Can access the collection with an index, and elements are stored contiguously in memory. | -An iterator also has the capability of the iterators that precede it in the table. For example, a `bidirectional_range` can be used with a `forward_range` iterator, but not vice versa. +An iterator also has the capability of the iterators that precede it in the table. For example, `bidirectional_range` can be used with a `forward_range` iterator, but not vice versa. -The statement "requires `input_range` or better", means that the view can be used with an `input_range`, `forward_range`, `bidirectional_range`, `random_access_range`, or `contiguous_range` iterator because any of those categories is as capable as an `input_range`. +The statement "requires `input_range` or better" means that the view can be used with an `input_range`, `forward_range`, `bidirectional_range`, `random_access_range`, or `contiguous_range` iterator, because any of those categories is as capable as `input_range`. ## See also [``](ranges.md)\ -[`range-adaptors`](range-adaptors.md) +[Range adaptors](range-adaptors.md)