为什么 std::get 对 ranges::subrange 只有两个函数重载?

Why does std::get only have two function overloads for ranges::subrange?

标准中有四种pair-like类型,分别是std::arraystd::pairstd::tupleranges::subrange, where the overload of std::get for ranges::subrange is defined in [range.subrange#access-10]:

template<size_t N, class I, class S, subrange_kind K>
  requires (N < 2)
  constexpr auto get(const subrange<I, S, K>& r);
template<size_t N, class I, class S, subrange_kind K>
  requires (N < 2)
  constexpr auto get(subrange<I, S, K>&& r);

Effects: Equivalent to:

if constexpr (N == 0)
  return r.begin(); 
else
  return r.end();

ranges::subrange::begin()有两个重载:

constexpr I begin() const requires copyable<I>;
[[nodiscard]] constexpr I begin() requires (!copyable<I>);

我注意到这个std::get只有两个重载,对于非常量左值引用没有对应的重载,导致我们无法申请std::get 到左值 input_range 与非 copyable 迭代器 (godbolt):

#include <ranges>
#include <sstream>

int main() {
  auto ints = std::istringstream{"42"};
  auto is = std::ranges::istream_view<int>(ints);
  std::ranges::input_range auto r = std::ranges::subrange(is);
  auto b  = r.begin();      // OK
  auto b2 = std::get<0>(r); // Error, passing 'const subrange' as 'this' argument discards qualifiers
}

那么为什么 std::getranges::subrange 只有两个函数重载?它是否遗漏了 ranges::subrange&const ranges::subrange&& 的重载?这是标准缺陷还是故意的?

作为一般规则,定义良好的代码中的完整格式 subrange 表示有效范围,如果它存储大小,则大小等于范围的大小。这反映在每个非默认构造函数的前提条件中(默认构造状态仍然可以部分形成)。由于引入了仅移动迭代器(因为非常量 begin 需要将迭代器移出),这有点混乱,但仍然是设计意图。

换句话说,subrangepairtuplearray 完全不同。这三个是没有语义的值的聚合,它们的 get 重载通过透明传播 cv 限定和值类别来反映这一点。另一方面,subrange 确实具有语义——它不仅仅是一对迭代器和哨兵。它的 get 只用于阅读,从不用于写作。这就是为什么 getsubrange returns 按值。

提供非常量左值 get 重载没有多大意义;通过可变引用返回是不可能的;通过引用 const 返回不同于其他一切(而且也令人惊讶)。按值返回意味着在唯一情况下它会有所不同,左值 subrange 上的 get 是破坏性操作,这将是非常意外的。