23 Iterators library [iterators]

23.5 Iterator adaptors [predef.iterators]

23.5.4 Common iterators [iterators.common]

23.5.4.1 Class template common_­iterator [common.iterator]

Class template common_­iterator is an iterator/sentinel adaptor that is capable of representing a non-common range of elements (where the types of the iterator and sentinel differ) as a common 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_t>;
// call fun on a range of 10 ints
fun(CI(counted_iterator(s.begin(), 10)), CI(default_sentinel));
— end example
 ]
namespace std {
  template<input_or_output_iterator I, sentinel_for<I> S>
    requires (!same_as<I, S> && copyable<I>)
  class common_iterator {
  public:
    constexpr common_iterator() = default;
    constexpr common_iterator(I i);
    constexpr common_iterator(S s);
    template<class I2, class S2>
      requires convertible_to<const I2&, I> && convertible_to<const S2&, S>
        constexpr common_iterator(const common_iterator<I2, S2>& x);

    template<class I2, class S2>
      requires convertible_to<const I2&, I> && convertible_to<const S2&, S> &&
               assignable_from<I&, const I2&> && assignable_from<S&, const S2&>
        common_iterator& operator=(const common_iterator<I2, S2>& x);

    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);

    template<class I2, sentinel_for<I> S2>
      requires sentinel_for<S, I2>
    friend bool operator==(
      const common_iterator& x, const common_iterator<I2, S2>& y);
    template<class I2, sentinel_for<I> S2>
      requires sentinel_for<S, I2> && equality_comparable_with<I, I2>
    friend bool operator==(
      const common_iterator& x, const common_iterator<I2, S2>& y);

    template<sized_sentinel_for<I> I2, sized_sentinel_for<I> S2>
      requires sized_sentinel_for<S, I2>
    friend iter_difference_t<I2> operator-(
      const common_iterator& x, const common_iterator<I2, S2>& y);

    friend iter_rvalue_reference_t<I> iter_move(const common_iterator& i)
      noexcept(noexcept(ranges::iter_move(declval<const I&>())))
        requires input_iterator<I>;
    template<indirectly_swappable<I> I2, class S2>
      friend void iter_swap(const common_iterator& x, const common_iterator<I2, S2>& y)
        noexcept(noexcept(ranges::iter_swap(declval<const I&>(), declval<const I2&>())));

  private:
    variant<I, S> v_;   // exposition only
  };

  template<class I, class S>
  struct incrementable_traits<common_iterator<I, S>> {
    using difference_type = iter_difference_t<I>;
  };

  template<input_iterator I, class S>
  struct iterator_traits<common_iterator<I, S>> {
    using iterator_concept = see below;
    using iterator_category = see below;
    using value_type = iter_value_t<I>;
    using difference_type = iter_difference_t<I>;
    using pointer = see below;
    using reference = iter_reference_t<I>;
  };
}

23.5.4.2 Associated types [common.iter.types]

The nested typedef-names of the specialization of iterator_­traits for common_­iterator<I, S> are defined as follows.
  • iterator_­concept denotes forward_­iterator_­tag if I models forward_­iterator; otherwise it denotes input_­iterator_­tag.
  • iterator_­category denotes forward_­iterator_­tag if iterator_­traits<I>​::​iterator_­category models derived_­from<forward_­iterator_­tag>; otherwise it denotes input_­iterator_­tag.
  • If the expression a.operator->() is well-formed, where a is an lvalue of type const common_­iterator<I, S>, then pointer denotes the type of that expression.
    Otherwise, pointer denotes void.

23.5.4.3 Constructors and conversions [common.iter.const]

constexpr common_iterator(I i);
Effects: Initializes v_­ as if by v_­{in_­place_­type<I>, std​::​move(i)}.
constexpr common_iterator(S s);
Effects: Initializes v_­ as if by v_­{in_­place_­type<S>, std​::​move(s)}.
template<class I2, class S2> requires convertible_­to<const I2&, I> && convertible_­to<const S2&, S> constexpr common_iterator(const common_iterator<I2, S2>& x);
Preconditions: x.v_­.valueless_­by_­exception() is false.
Effects: Initializes v_­ as if by v_­{in_­place_­index<i>, get<i>(x.v_­)}, where i is x.v_­.index().
template<class I2, class S2> requires convertible_­to<const I2&, I> && convertible_­to<const S2&, S> && assignable_­from<I&, const I2&> && assignable_­from<S&, const S2&> common_iterator& operator=(const common_iterator<I2, S2>& x);
Preconditions: x.v_­.valueless_­by_­exception() is false.
Effects: Equivalent to:
  • If v_­.index() == x.v_­.index(), then get<i>(v_­) = get<i>(x.v_­).
  • Otherwise, v_­.emplace<i>(get<i>(x.v_­)).
where i is x.v_­.index().
Returns: *this

23.5.4.4 Accessors [common.iter.access]

decltype(auto) operator*(); decltype(auto) operator*() const requires dereferenceable<const I>;
Preconditions: holds_­alternative<I>(v_­).
Effects: Equivalent to: return *get<I>(v_­);
decltype(auto) operator->() const requires see below;
The expression in the requires clause is equivalent to:
indirectly_readable<const I> &&
(requires(const I& i) { i.operator->(); } ||
 is_reference_v<iter_reference_t<I>> ||
 constructible_from<iter_value_t<I>, iter_reference_t<I>>)
Preconditions: holds_­alternative<I>(v_­).
Effects:
  • If I is a pointer type or if the expression get<I>(v_­).operator->() is well-formed, equivalent to: return get<I>(v_­);
  • Otherwise, if iter_­reference_­t<I> is a reference type, equivalent to:
    auto&& tmp = *get<I>(v_);
    return addressof(tmp);
    
  • Otherwise, equivalent to: return proxy(*get<I>(v_­)); where proxy is the exposition-only class:
    class proxy {
      iter_value_t<I> keep_;
      proxy(iter_reference_t<I>&& x)
        : keep_(std::move(x)) {}
    public:
      const iter_value_t<I>* operator->() const {
        return addressof(keep_);
      }
    };
    

23.5.4.5 Navigation [common.iter.nav]

common_iterator& operator++();
Preconditions: holds_­alternative<I>(v_­).
Effects: Equivalent to ++get<I>(v_­).
Returns: *this.
decltype(auto) operator++(int);
Preconditions: holds_­alternative<I>(v_­).
Effects: If I models forward_­iterator, equivalent to:
common_iterator tmp = *this;
++*this;
return tmp;
Otherwise, equivalent to: return get<I>(v_­)++;

23.5.4.6 Comparisons [common.iter.cmp]

template<class I2, sentinel_­for<I> S2> requires sentinel_­for<S, I2> friend bool operator==( const common_iterator& x, const common_iterator<I2, S2>& y);
Preconditions: x.v_­.valueless_­by_­exception() and y.v_­.valueless_­by_­exception() are each false.
Returns: true if i == j, and otherwise get<i>(x.v_­) == get<j>(y.v_­), where i is x.v_­.index() and j is y.v_­.index().
template<class I2, sentinel_­for<I> S2> requires sentinel_­for<S, I2> && equality_comparable_with<I, I2> friend bool operator==( const common_iterator& x, const common_iterator<I2, S2>& y);
Preconditions: x.v_­.valueless_­by_­exception() and y.v_­.valueless_­by_­exception() are each false.
Returns: true if i and j are each 1, and otherwise get<i>(x.v_­) == get<j>(y.v_­), where i is x.v_­.index() and j is y.v_­.index().
template<sized_­sentinel_­for<I> I2, sized_­sentinel_­for<I> S2> requires sized_­sentinel_­for<S, I2> friend iter_difference_t<I2> operator-( const common_iterator& x, const common_iterator<I2, S2>& y);
Preconditions: x.v_­.valueless_­by_­exception() and y.v_­.valueless_­by_­exception() are each false.
Returns: 0 if i and j are each 1, and otherwise get<i>(x.v_­) - get<j>(y.v_­), where i is x.v_­.index() and j is y.v_­.index().

23.5.4.7 Customization [common.iter.cust]

friend iter_rvalue_reference_t<I> iter_move(const common_iterator& i) noexcept(noexcept(ranges::iter_move(declval<const I&>()))) requires input_­iterator<I>;
Preconditions: holds_­alternative<I>(v_­).
Effects: Equivalent to: return ranges​::​iter_­move(get<I>(i.v_­));
template<indirectly_­swappable<I> I2, class S2> friend void iter_swap(const common_iterator& x, const common_iterator<I2, S2>& y) noexcept(noexcept(ranges::iter_swap(declval<const I&>(), declval<const I2&>())));
Preconditions: holds_­alternative<I>(x.v_­) and holds_­alternative<I2>(y.v_­) are each true.
Effects: Equivalent to ranges​::​iter_­swap(get<I>(x.v_­), get<I2>(y.v_­)).