[ranges.subrange] 中 `iterator-sentinel-pair` 概念的目的是什么?

What is the purpose of the `iterator-sentinel-pair` concept in [ranges.subrange]?

[range.subrange] 中定义的仅供说明的 iterator-sentinel-pair 概念的目的是什么?

template<class T> 
concept iterator-sentinel-pair = // exposition only 
  !range<T> && pair-like<T> && 
  sentinel_for<tuple_element_t<1, T>, tuple_element_t<0, T>>;

据我所知,唯一一次使用它是为了 CTAD,例如在

template<iterator-sentinel-pair P>
  subrange(P) -> subrange<tuple_element_t<0, P>, tuple_element_t<1, P>>;

但是,没有任何 std::ranges::subrange 的构造函数可以使用这个概念:唯一可能适合此参数列表的构造函数是 this one:

template<not-same-as<subrange> R> 
  requires borrowed_range<R> &&
           convertible-to-non-slicing<iterator_t<R>, I> &&
           convertible_to<sentinel_t<R>, S>
constexpr subrange(R&& r) requires (!StoreSize || sized_range<R>);

这要求(通过 borrowed_range<R>)我们有 range<R>...但是 iterator-sentinel-pair 明确要求 not range<R>!

在我看来,其目的是允许代码遵循

std::multiset foo = // ...
auto result = foo.equal_range(key);  // an iterator-sentinel pair
for (auto value : std::ranges::subrange(result)) {
  // ...
}

但这显然无法编译,因为 std::pair<It, It> 不满足 std::range 的要求。也许允许这个用例的构造函数被忽略了?

我还缺少其他目的吗?

What's the purpose of the exposition-only iterator-sentinel-pair concept as defined in [range.subrange]?

不再有任何用途,它们已从 LWG 3404 adopted at the previous plenary, and will shortly be removed from the draft. They're just vestigial oversight after the all the stuff that used them was removed in LWG 3281 起删除。


本来,这个概念的目的正是为了让这个:

for (auto value : std::ranges::subrange(foo.equal_range(key))) {
    // ...
}

但基于以下推理删除了隐式:

Just because a pair is holding two iterators, it doesn't mean those two iterators denote a valid range. Implicitly converting such pair-like types to a subrange is dangerous and should be disallowed.

虽然至少有一个 显式 转换似乎很有用。值得庆幸的是,我们自己写起来很简单:

template <typename P>
    requires (std::tuple_size_v<P> == 2)
          && std::ranges::sentinel_for<std::tuple_element_t<1, P>,
                                       std::tuple_element_t<0, P>>
auto make_subrange_from_pair(P pair) {
    // either this
    auto [b, e] = pair;
    return subrange(b, e);
    // or that
    return subrange(std::get<0>(pair), std::get<1>(pair));
}