C++20 introduces a ranges::split_view, which accepts a view and a delimiter, and splits the view into subranges according to the delimiter.
The delimiter is also a view, and when it is a single element, it will be wrapped in a ranges::single_view.
Each returned subrange is also a view, its type is split_view::outer-iterator::value_type, which is defined in [range.adaptors#range.split.outer.value]:
template<bool Const>
struct split_view<V, Pattern>::outer-iterator<Const>::value_type
: view_interface<value_type> {
private:
outer-iterator i_ = outer-iterator(); // exposition only
public:
value_type() = default;
constexpr explicit value_type(outer-iterator i) : i_(std::move(i)) {}
constexpr inner-iterator<Const> begin() const requires copyable<outer-iterator>
constexpr inner-iterator<Const> begin() requires (!copyable<outer-iterator>);
constexpr default_sentinel_t end() const;
};
The return type of begin() and end() of this inner view are inner-iterator and default_sentinel_t, but it is not important to the problem. The problem lies in the two constrained begin() functions here:
constexpr inner-iterator<Const> begin() const requires copyable<outer-iterator>
{ return inner-iterator<Const>{i_}; }
constexpr inner-iterator<Const> begin() requires (!copyable<outer-iterator>);
{ return inner-iterator<Const>{std::move(i_)}; }
The begin() function chooses whether to move the underlying i_ according to whether its type outer-iterator is copyable, which looks reasonable.
However, this constraint seems useless, the reason is that outer-iterator seems to always satisfy copyable. In [range.adaptors#range.split.outer], the outer-iterator, which is the iterator type of the split_view, is defined as:
template<bool Const>
struct split_view<V, Pattern>::outer-iterator {
private:
using Parent = maybe-const<Const, split_view>; // exposition only
using Base = maybe-const<Const, V>; // exposition only
Parent* parent_ = nullptr; // exposition only
iterator_t<Base> current_ = iterator_t<Base>(); // exposition only, present only if V models forward_range
public:
// ...
};
When the base type V of split_view does not models forward_range such as input_range, the outer-iterator only contains a pointer, so it is obviously copyable. When V is a forward_range, the current_ is also a forward_iterator, so it is copyable, which makes outer-iterator also copyable.
So why does the standard need to define a begin() function in split_view::outer-iterator::value_type based on a constraint that can never be satisfied?
I checked the earlier paper and did not find any discussion about this part, but this begin() function seems to be added after LWG3276.