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:
// ...
};
当基类型V不split_view模型forward_range如 时input_range,outer-iterator只包含一个指针,所以它显然是copyable。当V是 aforward_range时, thecurrent_也是 a forward_iterator, 所以它是copyable, 这 使得outer-iterator也copyable。
那么为什么标准需要基于一个永远无法满足的约束来定义一个begin()函数呢?split_view::outer-iterator::value_type
查了之前的论文,没有发现关于这部分的讨论,不过这个begin()功能好像是在LWG3276之后添加的。