10 Ranges library [ranges]

10.1 General [ranges.general]

This Clause describes components for dealing with ranges of elements.

The following subclauses describe range and view requirements, and components for range primitives as summarized in Table [tab:ranges.lib.summary].

Table 9 — Ranges library summary
Subclause Header(s)
[range.access] Range access <experimental/ranges/range>
[range.primitives] Range primitives
[ranges.requirements] Requirements

10.2 decay_copy [ranges.decaycopy]

Several places in this Clause use the expression DECAY_COPY(x), which is expression-equivalent to:

  decay_t<decltype((x))>(x)

10.3 Header <experimental/ranges/range> synopsis [range.synopsis]

#include <experimental/ranges/iterator>

namespace std { namespace experimental { namespace ranges { inline namespace v1 {
  // [range.access], range access:
  namespace {
    constexpr unspecified begin = unspecified;
    constexpr unspecified end = unspecified;
    constexpr unspecified cbegin = unspecified;
    constexpr unspecified cend = unspecified;
    constexpr unspecified rbegin = unspecified;
    constexpr unspecified rend = unspecified;
    constexpr unspecified crbegin = unspecified;
    constexpr unspecified crend = unspecified;
  }

  // [range.primitives], range primitives:
  namespace {
    constexpr unspecified size = unspecified;
    constexpr unspecified empty = unspecified;
    constexpr unspecified data = unspecified;
    constexpr unspecified cdata = unspecified;
  }

  template <class T>
  using iterator_t = decltype(ranges::begin(declval<T&>()));

  template <class T>
  using sentinel_t = decltype(ranges::end(declval<T&>()));

  template <class>
  constexpr bool disable_sized_range = false;

  template <class T>
  struct enable_view { };

  struct view_base { };

  // [ranges.requirements], range requirements:

  // [ranges.range], Range:
  template <class T>
  concept bool Range = see below;

  // [ranges.sized], SizedRange:
  template <class T>
  concept bool SizedRange = see below;

  // [ranges.view], View:
  template <class T>
  concept bool View = see below;

  // [ranges.bounded], BoundedRange:
  template <class T>
  concept bool BoundedRange = see below;

  // [ranges.input], InputRange:
  template <class T>
  concept bool InputRange = see below;

  // [ranges.output], OutputRange:
  template <class R, class T>
  concept bool OutputRange = see below;

  // [ranges.forward], ForwardRange:
  template <class T>
  concept bool ForwardRange = see below;

  // [ranges.bidirectional], BidirectionalRange:
  template <class T>
  concept bool BidirectionalRange = see below;

  // [ranges.random.access], RandomAccessRange:
  template <class T>
  concept bool RandomAccessRange = see below;
}}}}

10.4 Range access [range.access]

In addition to being available via inclusion of the <experimental/ranges/range> header, the customization point objects in [range.access] are available when <experimental/ranges/iterator> is included.

10.4.1 begin [range.access.begin]

The name begin denotes a customization point object ([customization.point.object]). The expression ranges::begin(E) for some subexpression E is expression-equivalent to:

  • ranges::begin(static_cast<const T&>(E)) if E is an rvalue of type T. This usage is deprecated. [ Note: This deprecated usage exists so that ranges::begin(E) behaves similarly to std::begin(E) as defined in ISO/IEC 14882 when E is an rvalue.  — end note ]

  • Otherwise, (E) + 0 if E has array type ( ISO/IEC 14882:2014 §[basic.compound]).

  • Otherwise, DECAY_COPY((E).begin()) if it is a valid expression and its type I meets the syntactic requirements of Iterator<I>. If Iterator is not satisfied, the program is ill-formed with no diagnostic required.

  • Otherwise, DECAY_COPY(begin(E)) if it is a valid expression and its type I meets the syntactic requirements of Iterator<I> with overload resolution performed in a context that includes the declaration void begin(auto&) = delete; and does not include a declaration of ranges::begin. If Iterator is not satisfied, the program is ill-formed with no diagnostic required.

  • Otherwise, ranges::begin(E) is ill-formed.

Note: Whenever ranges::begin(E) is a valid expression, its type satisfies Iterator.  — end note ]

10.4.2 end [range.access.end]

The name end denotes a customization point object ([customization.point.object]). The expression ranges::end(E) for some subexpression E is expression-equivalent to:

  • ranges::end(static_cast<const T&>(E)) if E is an rvalue of type T. This usage is deprecated. [ Note: This deprecated usage exists so that ranges::end(E) behaves similarly to std::end(E) as defined in ISO/IEC 14882 when E is an rvalue.  — end note ]

  • Otherwise, (E) + extent<T>::value if E has array type ( ISO/IEC 14882:2014 §[basic.compound]) T.

  • Otherwise, DECAY_COPY((E).end()) if it is a valid expression and its type S meets the syntactic requirements of Sentinel<S, decltype(ranges::begin(E))>. If Sentinel is not satisfied, the program is ill-formed with no diagnostic required.

  • Otherwise, DECAY_COPY(end(E)) if it is a valid expression and its type S meets the syntactic requirements of Sentinel<S, decltype(ranges::begin(E))> with overload resolution performed in a context that includes the declaration void end(auto&) = delete; and does not include a declaration of ranges::end. If Sentinel is not satisfied, the program is ill-formed with no diagnostic required.

  • Otherwise, ranges::end(E) is ill-formed.

Note: Whenever ranges::end(E) is a valid expression, the types of ranges::end(E) and ranges::begin(E) satisfy Sentinel.  — end note ]

10.4.3 cbegin [range.access.cbegin]

The name cbegin denotes a customization point object ([customization.point.object]). The expression ranges::cbegin(E) for some subexpression E of type T is expression-equivalent to ranges::begin(static_cast<const T&>(E)).

Use of ranges::cbegin(E) with rvalue E is deprecated. [ Note: This deprecated usage exists so that ranges::cbegin(E) behaves similarly to std::cbegin(E) as defined in ISO/IEC 14882 when E is an rvalue.  — end note ]

Note: Whenever ranges::cbegin(E) is a valid expression, its type satisfies Iterator.  — end note ]

10.4.4 cend [range.access.cend]

The name cend denotes a customization point object ([customization.point.object]). The expression ranges::cend(E) for some subexpression E of type T is expression-equivalent to ranges::end(static_cast<const T&>(E)).

Use of ranges::cend(E) with rvalue E is deprecated. [ Note: This deprecated usage exists so that ranges::cend(E) behaves similarly to std::cend(E) as defined in ISO/IEC 14882 when E is an rvalue.  — end note ]

Note: Whenever ranges::cend(E) is a valid expression, the types of ranges::cend(E) and ranges::cbegin(E) satisfy Sentinel.  — end note ]

10.4.5 rbegin [range.access.rbegin]

The name rbegin denotes a customization point object ([customization.point.object]). The expression ranges::rbegin(E) for some subexpression E is expression-equivalent to:

  • ranges::rbegin(static_cast<const T&>(E)) if E is an rvalue of type T. This usage is deprecated. [ Note: This deprecated usage exists so that ranges::rbegin(E) behaves similarly to std::rbegin(E) as defined in ISO/IEC 14882 when E is an rvalue.  — end note ]

  • Otherwise, DECAY_COPY((E).rbegin()) if it is a valid expression and its type I meets the syntactic requirements of Iterator<I>. If Iterator is not satisfied, the program is ill-formed with no diagnostic required.

  • Otherwise, make_reverse_iterator(ranges::end(E)) if both ranges::begin(E) and ranges::end(E) are valid expressions of the same type I which meets the syntactic requirements of BidirectionalIterator<I> ([iterators.bidirectional]).

  • Otherwise, ranges::rbegin(E) is ill-formed.

Note: Whenever ranges::rbegin(E) is a valid expression, its type satisfies Iterator.  — end note ]

10.4.6 rend [range.access.rend]

The name rend denotes a customization point object ([customization.point.object]). The expression ranges::rend(E) for some subexpression E is expression-equivalent to:

  • ranges::rend(static_cast<const T&>(E)) if E is an rvalue of type T. This usage is deprecated. [ Note: This deprecated usage exists so that ranges::rend(E) behaves similarly to std::rend(E) as defined in ISO/IEC 14882 when E is an rvalue.  — end note ]

  • Otherwise, DECAY_COPY((E).rend()) if it is a valid expression and its type S meets the syntactic requirements of Sentinel<S, decltype(ranges::rbegin(E))>. If Sentinel is not satisfied, the program is ill-formed with no diagnostic required.

  • Otherwise, make_reverse_iterator(ranges::begin(E)) if both ranges::begin(E) and ranges::end(E) are valid expressions of the same type I which meets the syntactic requirements of BidirectionalIterator<I> ([iterators.bidirectional]).

  • Otherwise, ranges::rend(E) is ill-formed.

Note: Whenever ranges::rend(E) is a valid expression, the types of ranges::rend(E) and ranges::rbegin(E) satisfy Sentinel.  — end note ]

10.4.7 crbegin [range.access.crbegin]

The name crbegin denotes a customization point object ([customization.point.object]). The expression ranges::crbegin(E) for some subexpression E of type T is expression-equivalent to ranges::rbegin(static_cast<const T&>(E)).

Use of ranges::crbegin(E) with rvalue E is deprecated. [ Note: This deprecated usage exists so that ranges::crbegin(E) behaves similarly to std::crbegin(E) as defined in ISO/IEC 14882 when E is an rvalue.  — end note ]

Note: Whenever ranges::crbegin(E) is a valid expression, its type satisfies Iterator.  — end note ]

10.4.8 crend [range.access.crend]

The name crend denotes a customization point object ([customization.point.object]). The expression ranges::crend(E) for some subexpression E of type T is expression-equivalent to ranges::rend(static_cast<const T&>(E)).

Use of ranges::crend(E) with rvalue E is deprecated. [ Note: This deprecated usage exists so that ranges::crend(E) behaves similarly to std::crend(E) as defined in ISO/IEC 14882 when E is an rvalue.  — end note ]

Note: Whenever ranges::crend(E) is a valid expression, the types of ranges::crend(E) and ranges::crbegin(E) satisfy Sentinel.  — end note ]

10.5 Range primitives [range.primitives]

In addition to being available via inclusion of the <experimental/ranges/range> header, the customization point objects in [range.primitives] are available when <experimental/ranges/iterator> is included.

10.5.1 size [range.primitives.size]

The name size denotes a customization point object ([customization.point.object]). The expression ranges::size(E) for some subexpression E with type T is expression-equivalent to:

  • DECAY_COPY(extent<T>::value) if T is an array type ( ISO/IEC 14882:2014 §[basic.compound]).

  • Otherwise, DECAY_COPY(static_cast<const T&>(E).size()) if it is a valid expression and its type I satisfies Integral<I> and disable_sized_range<T> ([ranges.sized]) is false.

  • Otherwise, DECAY_COPY(size(static_cast<const T&>(E))) if it is a valid expression and its type I satisfies Integral<I> with overload resolution performed in a context that includes the declaration void size(const auto&) = delete; and does not include a declaration of ranges::size, and disable_sized_range<T> is false.

  • Otherwise, DECAY_COPY(ranges::cend(E) - ranges::cbegin(E)), except that E is only evaluated once, if it is a valid expression and the types I and S of ranges::cbegin(E) and ranges::cend(E) meet the syntactic requirements of SizedSentinel<S, I> ([iterators.sizedsentinel]) and ForwardIterator<I>. If SizedSentinel and ForwardIterator are not satisfied, the program is ill-formed with no diagnostic required.

  • Otherwise, ranges::size(E) is ill-formed.

Note: Whenever ranges::size(E) is a valid expression, its type satisfies Integral.  — end note ]

10.5.2 empty [range.primitives.empty]

The name empty denotes a customization point object ([customization.point.object]). The expression ranges::empty(E) for some subexpression E is expression-equivalent to:

  • bool((E).empty()) if it is a valid expression.

  • Otherwise, ranges::size(E) == 0 if it is a valid expression.

  • Otherwise, bool(ranges::begin(E) == ranges::end(E)), except that E is only evaluated once, if it is a valid expression and the type of ranges::begin(E) satisfies ForwardIterator.

  • Otherwise, ranges::empty(E) is ill-formed.

Note: Whenever ranges::empty(E) is a valid expression, it has type bool.  — end note ]

10.5.3 data [range.primitives.data]

The name data denotes a customization point object ([customization.point.object]). The expression ranges::data(E) for some subexpression E is expression-equivalent to:

  • ranges::data(static_cast<const T&>(E)) if E is an rvalue of type T. This usage is deprecated. [ Note: This deprecated usage exists so that ranges::data(E) behaves similarly to std::data(E) as defined in the C++ Working Paper when E is an rvalue.  — end note ]

  • Otherwise, DECAY_COPY((E).data()) if it is a valid expression of pointer to object type.

  • Otherwise, ranges::begin(E) if it is a valid expression of pointer to object type.

  • Otherwise, ranges::data(E) is ill-formed.

Note: Whenever ranges::data(E) is a valid expression, it has pointer to object type.  — end note ]

10.5.4 cdata [range.primitives.cdata]

The name cdata denotes a customization point object ([customization.point.object]). The expression ranges::cdata(E) for some subexpression E of type T is expression-equivalent to ranges::data(static_cast<const T&>(E)).

Use of ranges::cdata(E) with rvalue E is deprecated. [ Note: This deprecated usage exists so that ranges::cdata(E) has behavior consistent with ranges::data(E) when E is an rvalue.  — end note ]

Note: Whenever ranges::cdata(E) is a valid expression, it has pointer to object type.  — end note ]

10.6 Range requirements [ranges.requirements]

10.6.1 General [ranges.requirements.general]

Ranges are an abstraction of containers that allow a C++ program to operate on elements of data structures uniformly. It their simplest form, a range object is one on which one can call begin and end to get an iterator ([iterators.iterator]) and a sentinel ([iterators.sentinel]). To be able to construct template algorithms and range adaptors that work correctly and efficiently on different types of sequences, the library formalizes not just the interfaces but also the semantics and complexity assumptions of ranges.

This document defines three fundamental categories of ranges based on the syntax and semantics supported by each: range, sized range and view, as shown in Table [tab:ranges.relations].

Table 10 — Relations among range categories
Sized Range
Range
View

The Range concept requires only that begin and end return an iterator and a sentinel. The SizedRange concept refines Range with the requirement that the number of elements in the range can be determined in constant time using the size function. The View concept specifies requirements on a Range type with constant-time copy and assign operations.

In addition to the three fundamental range categories, this document defines a number of convenience refinements of Range that group together requirements that appear often in the concepts and algorithms. Bounded ranges are ranges for which begin and end return objects of the same type. Random access ranges are ranges for which begin returns a type that satisfies RandomAccessIterator ([iterators.random.access]). The range categories bidirectional ranges, forward ranges, input ranges, and output ranges are defined similarly.

10.6.2 Ranges [ranges.range]

The Range concept defines the requirements of a type that allows iteration over its elements by providing a begin iterator and an end sentinel. [ Note: Most algorithms requiring this concept simply forward to an Iterator-based algorithm by calling begin and end.  — end note ]

template <class T> concept bool Range = requires(T&& t) { ranges::begin(t); // not necessarily equality-preserving (see below) ranges::end(t); };

Given an lvalue t of type remove_reference_t<T>, Range<T> is satisfied only if

  • [begin(t),end(t)) denotes a range.

  • Both begin(t) and end(t) are amortized constant time and non-modifying. [ Note: begin(t) and end(t) do not require implicit expression variations ([concepts.lib.general.equality]).  — end note ]

  • If iterator_t<T> satisfies ForwardIterator, begin(t) is equality preserving.

Note: Equality preservation of both begin and end enables passing a Range whose iterator type satisfies ForwardIterator to multiple algorithms and making multiple passes over the range by repeated calls to begin and end. Since begin is not required to be equality preserving when the return type does not satisfy ForwardIterator, repeated calls might not return equal values or might not be well-defined; begin should be called at most once for such a range.  — end note ]

10.6.3 Sized ranges [ranges.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 bool SizedRange = Range<T> && !disable_sized_range<remove_cv_t<remove_reference_t<T>>> && requires(T& t) { { ranges::size(t) } -> ConvertibleTo<difference_type_t<iterator_t<T>>>; };

Given an lvalue t of type remove_reference_t<T>, SizedRange<T> is satisfied only if:

  • ranges::size(t) is Ο(1), does not modify t, and is equal to ranges::distance(t).

  • If iterator_t<T> satisfies ForwardIterator, size(t) is well-defined regardless of the evaluation of begin(t). [ Note: size(t) is otherwise not required be well-defined after evaluating begin(t). For a SizedRange whose iterator type does not model ForwardIterator, for example, size(t) might only be well-defined if evaluated before the first call to begin(t).  — end note ]

Note: The disable_sized_range predicate provides a mechanism to enable use of range types with the library that meet the syntactic requirements but do not in fact satisfy SizedRange. A program that instantiates a library template that requires a Range with such a range type R is ill-formed with no diagnostic required unless disable_sized_range<remove_cv_t<remove_reference_t<R>>> evaluates to true ([structure.requirements]).  — end note ]

10.6.4 Views [ranges.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.

A container ( ISO/IEC 14882:2014 §[containers]) is not a View since copying the container copies the elements, which cannot be done in constant time.  — end example ]

template <class T> constexpr bool view-predicate // exposition only = see below; template <class T> concept bool View = Range<T> && Semiregular<T> && view-predicate<T>;

Since the difference between Range and View is largely semantic, the two are differentiated with the help of the enable_view trait. Users may specialize enable_view to derive from true_type or false_type.

For a type T, the value of view-predicate<T> shall be:

  • If enable_view<T> has a member type type, enable_view<T>::type::value;

  • Otherwise, if T is derived from view_base, true;

  • Otherwise, if T is an instantiation of class template initializer_list ( ISO/IEC 14882:2014 §[support.initlist]), set ( ISO/IEC 14882:2014 §[set]), multiset ( ISO/IEC 14882:2014 §[multiset]), unordered_set ( ISO/IEC 14882:2014 §[unord.set]), or unordered_multiset ( ISO/IEC 14882:2014 §[unord.multiset]), false;

  • Otherwise, if both T and const T satisfy Range and reference_t<iterator_t<T>> is not the same type as reference_t<iterator_t<const T>>, false; [ Note: Deep const-ness implies element ownership, whereas shallow const-ness implies reference semantics.  — end note ]

  • Otherwise, true.

10.6.5 Bounded ranges [ranges.bounded]

The BoundedRange concept specifies requirements of a Range type for which begin and end return objects of the same type. [ Note: The standard containers ( ISO/IEC 14882:2014 §[containers]) satisfy BoundedRange. — end note ]

template <class T>
concept bool BoundedRange =
  Range<T> && Same<iterator_t<T>, sentinel_t<T>>;

10.6.6 Input ranges [ranges.input]

The InputRange concept specifies requirements of a Range type for which begin returns a type that satisfies InputIterator ([iterators.input]).

template <class T>
concept bool InputRange =
  Range<T> && InputIterator<iterator_t<T>>;

10.6.7 Output ranges [ranges.output]

The OutputRange concept specifies requirements of a Range type for which begin returns a type that satisfies OutputIterator ([iterators.output]).

template <class R, class T>
concept bool OutputRange =
  Range<R> && OutputIterator<iterator_t<R>, T>;

10.6.8 Forward ranges [ranges.forward]

The ForwardRange concept specifies requirements of an InputRange type for which begin returns a type that satisfies ForwardIterator ([iterators.forward]).

template <class T>
concept bool ForwardRange =
  InputRange<T> && ForwardIterator<iterator_t<T>>;

10.6.9 Bidirectional ranges [ranges.bidirectional]

The BidirectionalRange concept specifies requirements of a ForwardRange type for which begin returns a type that satisfies BidirectionalIterator ([iterators.bidirectional]).

template <class T>
concept bool BidirectionalRange =
  ForwardRange<T> && BidirectionalIterator<iterator_t<T>>;

10.6.10 Random access ranges [ranges.random.access]

The RandomAccessRange concept specifies requirements of a BidirectionalRange type for which begin returns a type that satisfies RandomAccessIterator ([iterators.random.access]).

template <class T>
concept bool RandomAccessRange =
  BidirectionalRange<T> && RandomAccessIterator<iterator_t<T>>;