Skip to content

Commit be20e45

Browse files
TylerMSFTTylerMSFT
authored andcommitted
structure for range iterators
1 parent b6d3188 commit be20e45

2 files changed

Lines changed: 153 additions & 0 deletions

File tree

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
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)

docs/standard-library/toc.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -928,6 +928,8 @@ items:
928928
href: ranges.md
929929
- name: <ranges> functions
930930
href: range-functions.md
931+
- name: <ranges> iterators
932+
href: range-iterators.md
931933
- name: <ratio>
932934
href: ratio.md
933935
- name: <regex>

0 commit comments

Comments
 (0)