什么情况下ranges::split_view的迭代器不满足copyable?
C++20 引入了 a ranges::split_view,它接受 aview和一个定界符,并view根据定界符将 a分成子范围。
分隔符也是 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()和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是 a 时forward_range,thecurrent_也是 a forward_iterator,所以是copyable,这使得outer-iterator也是copyable。
那么为什么标准需要根据永远无法满足的约束来定义一个begin()函数split_view::outer-iterator::value_type呢?
查了之前的论文,没有发现这部分的讨论,不过这个begin()功能好像是在LWG3276之后加的。