The range concept defines the requirements of a type that allows
iteration over its elements by providing an iterator and sentinel
that denote the elements of the range.

```
template<class T>
concept range-impl = // exposition only
requires(T&& t) {
ranges::begin(std::forward<T>(t)); // sometimes equality-preserving (see below)
ranges::end(std::forward<T>(t));
};
template<class T>
concept range = range-impl<T&>;
template<class T>
concept forwarding-range = // exposition only
range<T> && range-impl<T>;
```

The required expressions
ranges::begin(std::forward<T>(t))
and
ranges::end(std::forward<T>(t))
of the range-impl concept
do not require implicit expression variations ([concepts.equality]).

Given an expression E such that decltype((E)) is T,
T models range-impl only if

- [ranges::begin(E), ranges::end(E)) denotes a range ([iterator.requirements.general]),
- both ranges::begin(E) and ranges::end(E) are amortized constant time and non-modifying, and
- if the type of ranges::begin(E) models forward_iterator, ranges::begin(E) is equality-preserving.

[ Note

: *end note*

]Equality preservation of both ranges::begin and
ranges::end enables passing a range whose iterator
type models forward_iterator to multiple
algorithms and making multiple passes over the range by repeated calls to
ranges::begin and ranges::end.

Since ranges::begin is not required to be equality-preserving
when the return type does not model forward_iterator, repeated calls
might not return equal values or might not be well-defined;
ranges::begin should be called at most once for such a range.

â€” Given an expression E such that decltype((E)) is T
and an lvalue t that denotes the same object as E,
T models forwarding-range only if

- ranges::begin(E) and ranges::begin(t) are expression-equivalent,
- ranges::end(E) and ranges::end(t) are expression-equivalent, and
- the validity of iterators obtained from the object denoted by E is not tied to the lifetime of that object.