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 ]
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);
}}}}
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.
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
decltype(auto) operator*();
decltype(auto) operator*() const
requires dereferenceable<const I>;
Requires: !is_sentinel
Effects: Equivalent to: return *iter;
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>>)
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;
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);
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))
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))