24 Ranges library [ranges]

24.4 Range requirements [range.req]

24.4.1 General [range.req.general]

Ranges are an abstraction that allow a C++ program to operate on elements of data structures uniformly.
Calling ranges::begin on a range returns an object whose type models Iterator.
Calling ranges::end on a range returns an object whose type S, together with the type I of the object returned by ranges::begin, models Sentinel<S, I>.
The library formalizes the interfaces, semantics, and complexity of ranges to enable algorithms and range adaptors that work efficiently on different types of sequences.
The Range concept requires that ranges::begin and ranges::end return an iterator and a sentinel, respectively.
The SizedRange concept refines Range with the requirement that the number of elements in the range can be determined in constant time using the ranges::size function.
The View concept specifies requirements on a Range type with constant-time copy and assign operations.
Several refinements of Range group requirements that arise frequently in concepts and algorithms.
Common ranges are ranges for which ranges::begin and ranges::end return objects of the same type.
Random access ranges are ranges for which ranges::begin returns a type that models RandomAccessIterator.
(Contiguous, bidirectional, forward, input, and output ranges are defined similarly.)
Viewable ranges can be converted to views.

24.4.2 Ranges [range.range]

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 ForwardIterator, ranges::begin(E) is equality-preserving.
[ Note
:
Equality preservation of both ranges::begin and ranges::end enables passing a Range whose iterator type models ForwardIterator 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 ForwardIterator, 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.
— end note
 ]
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.
[ Note
:
Since the validity of iterators is not tied to the lifetime of an object whose type models forwarding-range, a function can accept arguments of such a type by value and return iterators obtained from it without danger of dangling.
— end note
 ]
[ Example
:
Specializations of class template subrange model forwarding-range.
subrange provides non-member rvalue overloads of begin and end with the same semantics as its member lvalue overloads, and subrange's iterators - since they are β€œborrowed” from some other range - do not have validity tied to the lifetime of a subrange object.
— end example
 ]

24.4.3 Sized ranges [range.sized]

The SizedRange concept specifies the requirements of a Range type that knows its size in constant time with the size function.
template<class T> concept SizedRange = Range<T> && !disable_sized_range<remove_cvref_t<T>> && requires(T& t) { ranges::size(t); };
Given an lvalue t of type remove_­reference_­t<T>, T models SizedRange only if
  • ranges::size(t) is , does not modify t, and is equal to ranges::distance(t), and
  • if iterator_­t<T> models ForwardIterator, ranges::size(t) is well-defined regardless of the evaluation of ranges::begin(t).
    [ Note
    :
    ranges::size(t) is otherwise not required to be well-defined after evaluating ranges::begin(t).
    For example, ranges::size(t) might be well-defined for a SizedRange whose iterator type does not model ForwardIterator only if evaluated before the first call to ranges::begin(t).
    — end note
     ]
[ Note
:
The complexity requirement for the evaluation of ranges::size is non-amortized, unlike the case for the complexity of the evaluations of ranges::begin and ranges::end in the Range concept.
— end note
 ]
[ Note
:
disable_­sized_­range allows use of range types with the library that satisfy but do not in fact model SizedRange.
— end note
 ]

24.4.4 Views [range.view]

The View concept specifies the requirements of a Range type that has constant time copy, move, and assignment operators; that is, the cost of these operations is not proportional to the number of elements in the View.
[ Example
:
Examples of Views are:
  • A Range type that wraps a pair of iterators.
  • A Range type that holds its elements by shared_­ptr and shares ownership with all its copies.
  • A Range type that generates its elements on demand.
Most containers are not views since copying the container copies the elements, which cannot be done in constant time.
— end example
 ]
template<class T> inline constexpr bool enable_view = see below; template<class T> concept View = Range<T> && Semiregular<T> && enable_view<T>;
Since the difference between Range and View is largely semantic, the two are differentiated with the help of enable_­view.
For a type T, the default value of enable_­view<T> is:
Pursuant to [namespace.std], users may specialize enable_­view to true for types which model View, and false for types which do not.

24.4.5 Other range refinements [range.refinements]

The OutputRange concept specifies requirements of a Range type for which ranges::begin returns a model of OutputIterator.
InputRange, ForwardRange, BidirectionalRange, and RandomAccessRange are defined similarly.
template<class R, class T> concept OutputRange = Range<R> && OutputIterator<iterator_t<R>, T>; template<class T> concept InputRange = Range<T> && InputIterator<iterator_t<T>>; template<class T> concept ForwardRange = InputRange<T> && ForwardIterator<iterator_t<T>>; template<class T> concept BidirectionalRange = ForwardRange<T> && BidirectionalIterator<iterator_t<T>>; template<class T> concept RandomAccessRange = BidirectionalRange<T> && RandomAccessIterator<iterator_t<T>>;
ContiguousRange additionally requires that the ranges::data customization point ([range.prim.data]) is usable with the range.
template<class T> concept ContiguousRange = RandomAccessRange<T> && ContiguousIterator<iterator_t<T>> && requires(T& t) { { ranges::data(t) } -> Same<add_pointer_t<iter_reference_t<iterator_t<T>>>>; };
The CommonRange concept specifies requirements of a Range type for which ranges::begin and ranges::end return objects of the same type.
[ Example
:
The standard containers model CommonRange.
— end example
 ]
template<class T> concept CommonRange = Range<T> && Same<iterator_t<T>, sentinel_t<T>>;
The ViewableRange concept specifies the requirements of a Range type that can be converted to a View safely.
template<class T> concept ViewableRange = Range<T> && (forwarding-range<T> || View<decay_t<T>>);