9 Iterators library [iterators]

9.7 Iterator adaptors [iterators.predef]

9.7.4 Common iterators [iterators.common]

Class template common_iterator is an iterator/sentinel adaptor that is capable of representing a non-bounded range of elements (where the types of the iterator and sentinel differ) as a bounded range (where they are the same). It does this by holding either an iterator or a sentinel, and implementing the equality comparison operators appropriately.

Note: The common_iterator type is useful for interfacing with legacy code that expects the begin and end of a range to have the same type. — end note ]

Example:

template <class ForwardIterator>
void fun(ForwardIterator begin, ForwardIterator end);

list<int> s;
// populate the list s
using CI =
  common_iterator<counted_iterator<list<int>::iterator>,
                  default_sentinel>;
// call fun on a range of 10 ints
fun(CI(make_counted_iterator(s.begin(), 10)),
    CI(default_sentinel()));

 — end example ]

9.7.4.1 Class template common_iterator [common.iterator]

namespace std { namespace experimental { namespace ranges { inline namespace v1 {
  template <Iterator I, Sentinel<I> S>
    requires !Same<I, S>
  class common_iterator {
  public:
    using difference_type = difference_type_t<I>;

    constexpr common_iterator();
    constexpr common_iterator(I i);
    constexpr common_iterator(S s);
    constexpr common_iterator(const common_iterator<ConvertibleTo<I>, ConvertibleTo<S>>& u);
    common_iterator& operator=(const common_iterator<ConvertibleTo<I>, ConvertibleTo<S>>& u);

    decltype(auto) operator*();
    decltype(auto) operator*() const
      requires dereferenceable<const I>;
    decltype(auto) operator->() const
      requires see below;

    common_iterator& operator++();
    decltype(auto) operator++(int);
    common_iterator operator++(int)
      requires ForwardIterator<I>;

    friend rvalue_reference_t<I> iter_move(const common_iterator& i)
      noexcept(see below)
        requires InputIterator<I>;
    template <IndirectlySwappable<I> I2, class S2>
      friend void iter_swap(const common_iterator& x, const common_iterator<I2, S2>& y)
        noexcept(see below);

  private:
    bool is_sentinel; // exposition only
    I iter;           // exposition only
    S sentinel;       // exposition only
  };

  template <Readable I, class S>
  struct value_type<common_iterator<I, S>> {
    using type = value_type_t<I>;
  };

  template <InputIterator I, class S>
  struct iterator_category<common_iterator<I, S>> {
    using type = input_iterator_tag;
  };

  template <ForwardIterator I, class S>
  struct iterator_category<common_iterator<I, S>> {
    using type = forward_iterator_tag;
  };

  template <class I1, class I2, Sentinel<I2> S1, Sentinel<I1> S2>
  bool operator==(
    const common_iterator<I1, S1>& x, const common_iterator<I2, S2>& y);
  template <class I1, class I2, Sentinel<I2> S1, Sentinel<I1> S2>
    requires EqualityComparableWith<I1, I2>
  bool operator==(
    const common_iterator<I1, S1>& x, const common_iterator<I2, S2>& y);
  template <class I1, class I2, Sentinel<I2> S1, Sentinel<I1> S2>
  bool operator!=(
    const common_iterator<I1, S1>& x, const common_iterator<I2, S2>& y);

  template <class I2, SizedSentinel<I2> I1, SizedSentinel<I2> S1, SizedSentinel<I1> S2>
  difference_type_t<I2> operator-(
    const common_iterator<I1, S1>& x, const common_iterator<I2, S2>& y);
}}}}

9.7.4.2 common_iterator operations [common.iter.ops]

9.7.4.2.1 common_iterator constructors [common.iter.op.const]

constexpr common_iterator();

Effects: Constructs a common_iterator, value-initializing is_sentinel, iter, and sentinel. Iterator operations applied to the resulting iterator have defined behavior if and only if the corresponding operations are defined on a value-initialized iterator of type I.

constexpr common_iterator(I i);

Effects: Constructs a common_iterator, initializing is_sentinel with false, iter with i, and value-initializing sentinel.

constexpr common_iterator(S s);

Effects: Constructs a common_iterator, initializing is_sentinel with true, value-initializing iter, and initializing sentinel with s.

constexpr common_iterator(const common_iterator<ConvertibleTo<I>, ConvertibleTo<S>>& u);

Effects: Constructs a common_iterator, initializing is_sentinel with u.is_sentinel, iter with u.iter, and sentinel with u.sentinel.

9.7.4.2.2 common_iterator::operator= [common.iter.op=]

common_iterator& operator=(const common_iterator<ConvertibleTo<I>, ConvertibleTo<S>>& u);

Effects: Assigns u.is_sentinel to is_sentinel, u.iter to iter, and u.sentinel to sentinel.

Returns: *this

9.7.4.2.3 common_iterator::operator* [common.iter.op.star]

decltype(auto) operator*(); decltype(auto) operator*() const requires dereferenceable<const I>;

Requires: !is_sentinel

Effects: Equivalent to: return *iter;

9.7.4.2.4 common_iterator::operator-> [common.iter.op.ref]

decltype(auto) operator->() const requires see below;

Requires: !is_sentinel

Effects: Equivalent to:

  • If I is a pointer type or if the expression i.operator->() is well-formed, return iter;

  • Otherwise, if the expression *iter is a glvalue:

    auto&& tmp = *iter;
    return addressof(tmp);
    
  • Otherwise, return proxy(*iter); where proxy is the exposition-only class:

    class proxy {               // exposition only
      value_type_t<I> keep_;
      proxy(reference_t<I>&& x)
        : keep_(std::move(x)) {}
    public:
      const value_type_t<I>* operator->() const {
        return addressof(keep_);
      }
    };
    

The expression in the requires clause is equivalent to:

Readable<const I> &&
  (requires(const I& i) { i.operator->(); } ||
   is_reference<reference_t<I>>::value ||
   Constructible<value_type_t<I>, reference_t<I>>)

9.7.4.2.5 common_iterator::operator++ [common.iter.op.incr]

common_iterator& operator++();

Requires: !is_sentinel

Effects: Equivalent to ++iter.

Returns: *this.

decltype(auto) operator++(int);

Requires: !is_sentinel.

Effects: Equivalent to: return iter++;

common_iterator operator++(int) requires ForwardIterator<I>;

Requires: !is_sentinel

Effects: Equivalent to:

common_iterator tmp = *this;
++iter;
return tmp;

9.7.4.2.6 common_iterator comparisons [common.iter.op.comp]

template <class I1, class I2, Sentinel<I2> S1, Sentinel<I1> S2> bool operator==( const common_iterator<I1, S1>& x, const common_iterator<I2, S2>& y);

Effects: Equivalent to:

  return x.is_sentinel ?
    (y.is_sentinel || y.iter == x.sentinel) :
    (!y.is_sentinel || x.iter == y.sentinel);

template <class I1, class I2, Sentinel<I2> S1, Sentinel<I1> S2> requires EqualityComparableWith<I1, I2> bool operator==( const common_iterator<I1, S1>& x, const common_iterator<I2, S2>& y);

Effects: Equivalent to:

  return x.is_sentinel ?
    (y.is_sentinel || y.iter == x.sentinel) :
    (y.is_sentinel ?
        x.iter == y.sentinel :
        x.iter == y.iter);

template <class I1, class I2, Sentinel<I2> S1, Sentinel<I1> S2> bool operator!=( const common_iterator<I1, S1>& x, const common_iterator<I2, S2>& y);

Effects: Equivalent to: return !(x == y);

template <class I2, SizedSentinel<I2> I1, SizedSentinel<I2> S1, SizedSentinel<I1> S2> difference_type_t<I2> operator-( const common_iterator<I1, S1>& x, const common_iterator<I2, S2>& y);

Effects: Equivalent to:

  return x.is_sentinel ?
    (y.is_sentinel ? 0 : x.sentinel - y.iter) :
    (y.is_sentinel ?
         x.iter - y.sentinel :
         x.iter - y.iter);

9.7.4.2.7 iter_move [common.iter.op.iter_move]

friend rvalue_reference_t<I> iter_move(const common_iterator& i) noexcept(see below) requires InputIterator<I>;

Requires: !i.is_sentinel.

Effects: Equivalent to: return ranges::iter_move(i.iter);

Remarks: The expression in noexcept is equivalent to:

noexcept(ranges::iter_move(i.iter))

9.7.4.2.8 iter_swap [common.iter.op.iter_swap]

template <IndirectlySwappable<I> I2> friend void iter_swap(const common_iterator& x, const common_iterator<I2>& y) noexcept(see below);

Requires: !x.is_sentinel && !y.is_sentinel.

Effects: Equivalent to ranges::iter_swap(x.iter, y.iter).

Remarks: The expression in noexcept is equivalent to:

noexcept(ranges::iter_swap(x.iter, y.iter))