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()
view
inner-iterator
default_sentinel_t
begin()
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-iterator
copyable
但是,这个约束似乎没用,原因是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之后添加的。