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.