|
| 1 | +JTW - stub topic to convert to range-iterators. |
| 2 | + |
| 3 | + |
| 4 | +--- |
| 5 | +description: "Learn more about range concepts and range iterator concepts." |
| 6 | +title: "Concepts for ranges and range iterators" |
| 7 | +ms.date: 09/22/2022 |
| 8 | +f1_keywords: ["ranges/std::ranges::range"] |
| 9 | +helpviewer_keywords: ["std::ranges [C++], range"] |
| 10 | +--- |
| 11 | +# Concepts for <ranges> |
| 12 | + |
| 13 | +Concepts are are C++20 language feature used to 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. |
| 14 | + |
| 15 | +Consider the following example, which defines a concept to prevent instantiating a template with types that can't be divided: |
| 16 | + |
| 17 | +```cpp |
| 18 | +// requires /std:c++20 or later |
| 19 | + |
| 20 | +// Definition of the concept "dividable" that requires |
| 21 | +// that arguments a & b can be divided |
| 22 | +template <typename T> |
| 23 | +concept dividable = requires (T a, T b) |
| 24 | +{ |
| 25 | + a / b; |
| 26 | +}; |
| 27 | + |
| 28 | +// Apply the concept to a template |
| 29 | +// The template will only be instantiated if the argument T can do division |
| 30 | +// This prevents the template from being instantiated with types that can't be divided |
| 31 | +// This could have been applied to the parameter of a template function, but because |
| 32 | +// most of the concepts in the <ranges> library are applied to classes, this form is used |
| 33 | +template <class T> requires dividable<T> |
| 34 | +class DivideEmUp |
| 35 | +{ |
| 36 | +public: |
| 37 | + T Divide(T x, T y) |
| 38 | + { |
| 39 | + return x / y; |
| 40 | + } |
| 41 | +}; |
| 42 | + |
| 43 | +void main() |
| 44 | +{ |
| 45 | + DivideEmUp<int> dividerOfInts; |
| 46 | + std::cout << dividerOfInts.Divide(6, 3); // outputs: 2 |
| 47 | + // The following line will not compile because the template can't be instantiated |
| 48 | + // with char* because char* can be divided |
| 49 | + DivideEmUp<char*> dividerOfCharPtrs; // compiler error: cannot deduce template arguments |
| 50 | +} |
| 51 | +``` |
| 52 | + |
| 53 | +The following concepts are defined in `std::ranges` and are declared in the `<ranges>` header file. They are used in the declarations of [range adaptors](range-adaptors.md), [views](views.md), and so on. |
| 54 | + |
| 55 | +| **Range concept ** | **Description** | |
| 56 | +|--|--| |
| 57 | +| [`range`](#range)<sup>C++20</sup> | A `range` is a type that can be iterated. | |
| 58 | +| [`borrowed_range`](#borrowed_range)<sup>C++20</sup> | The validity of the iterators for a `borrowed_range` are not tied to the object's lifetime. | |
| 59 | +| [`sized_range`](#sized_range)<sup>C++20</sup> | A `range` that can provide the number of its elements in amortized constant time via `ranges::size` | |
| 60 | + |
| 61 | +## `borrowed_range` |
| 62 | + |
| 63 | +A type models `borrowed_range` if the validity of iterators you get from the object can outlive the lifetime of the object. |
| 64 | + |
| 65 | +```cpp |
| 66 | +template<class T> |
| 67 | +concept borrowed_range = |
| 68 | + range<T> && |
| 69 | + (is_lvalue_reference_v<T> || enable_borrowed_range<remove_cvref_t<T>>); |
| 70 | +``` |
| 71 | + |
| 72 | +### Parameters |
| 73 | + |
| 74 | +*`T`*\ |
| 75 | +The type to test to see if it is a `borrowed_range`. |
| 76 | + |
| 77 | +## `range` |
| 78 | + |
| 79 | +Defines the requirements a type must meet to be a `range`. A `range` provides an iterator and a sentinel for iterating over the elements in the `range`. |
| 80 | + |
| 81 | +```cpp |
| 82 | +template<class R> |
| 83 | +concept range = requires(T& rg) |
| 84 | +{ |
| 85 | + ranges::begin(rg); |
| 86 | + ranges::end(rg); |
| 87 | +}; |
| 88 | +``` |
| 89 | + |
| 90 | +### Parameters |
| 91 | + |
| 92 | +*`T`*\ |
| 93 | +The type to test to see if it is a `range`. |
| 94 | + |
| 95 | +*`rg`*\ |
| 96 | +An instance of `R` to test. |
| 97 | + |
| 98 | +### Remarks |
| 99 | + |
| 100 | +The requirements of a `range` are: |
| 101 | +- it can be iterated using `std::ranges::begin()` and `std::ranges::end()` |
| 102 | +- `ranges::begin(rg)` and `ranges::end(rg)` 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. |
| 103 | +- \[`ranges::begin(rg)`, `ranges::end(rg)`) denotes a valid range. |
| 104 | + |
| 105 | + |
| 106 | +## `sized_range` |
| 107 | + |
| 108 | +A `sized_range` can provide the number of its elements in amortized constant time via `ranges::size`. |
| 109 | + |
| 110 | +```cpp |
| 111 | +template<class T> |
| 112 | + concept sized_range = range<T> && |
| 113 | + requires(T& t) { ranges::size(t); }; |
| 114 | +``` |
| 115 | +
|
| 116 | +### Parameters |
| 117 | +
|
| 118 | +*`T`*\ |
| 119 | +The type to test to see if it is a `sized_range`. |
| 120 | +
|
| 121 | +*`t`*\ |
| 122 | +An instance of `T` to test. |
| 123 | +
|
| 124 | +### Remarks |
| 125 | +
|
| 126 | +The requirements of a `sized_range` are that calling `ranges::size` on it: |
| 127 | +- Doesn't modify the `range`. |
| 128 | +- Returns the number of elements in amortized constant time. 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. |
| 129 | +
|
| 130 | +### Example `sized_range` |
| 131 | +
|
| 132 | +The following example demonstrates that a vector of ints satisfies the requirements of a `sized_range`: |
| 133 | +
|
| 134 | +```cpp |
| 135 | +// requires /std:c++20 or later |
| 136 | +#include <ranges> |
| 137 | +#include <iostream> |
| 138 | +#include <vector> |
| 139 | +
|
| 140 | +int main() |
| 141 | +{ |
| 142 | + std::cout << boolalpha << std::ranges::sized_range<std::vector<int>> << '\n'; // outputs: true |
| 143 | +} |
| 144 | +``` |
| 145 | + |
| 146 | + |
| 147 | +## See also |
| 148 | + |
| 149 | +[`<ranges>`](ranges.md)\ |
| 150 | +[Range adaptors](range-adaptors.md)\ |
| 151 | +[View classes](view-classes.md) |
0 commit comments