5

C++20 引入了 a ranges::split_view,它接受 aview和分隔符,并view根据分隔符将其拆分为子范围。

分隔符也是a view,当它是单个元素时,它会被包裹在a中ranges::single_view

每个返回的子范围也是一个view,它的类型是split_view::outer-iterator::value_type,在[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;
};

这个内部的begin()and的返回类型是and ,但这对问题并不重要。问题在于这里的两个约束函数:end()viewinner-iteratordefault_sentinel_tbegin()

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_)­}; }

该函数根据其类型 是否begin()为 来选择是否移动底层,这看起来很合理。i_outer-iteratorcopyable

但是,这个约束似乎没用,原因是outer-iterator似乎总是满足copyable. 在[range.adaptors#range.split.outer]中,outer-iterator作为 的迭代器类型的split_view被定义为:

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:
// ...
};

当基类型Vsplit_view模型forward_range如 时input_rangeouter-iterator只包含一个指针,所以它显然是copyable。当V是 aforward_range时, thecurrent_也是 a forward_iterator, 所以它是copyable, 这 使得outer-iteratorcopyable

那么为什么标准需要基于一个永远无法满足的约束来定义一个begin()函数呢?split_view::outer-iterator::value_type

查了之前的论文,没有发现关于这部分的讨论,不过这个begin()功能好像是在LWG3276之后添加的。

4

0 回答 0