5

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.

康桓瑋
  • 33,481
  • 5
  • 40
  • 90

0 Answers0