首页 > 解决方案 > 为什么 std::get 只有两个 range::subrange 函数重载?

问题描述

标准中有四种 pair-like 类型,即std::arraystd::pairstd::tupleranges::subrange,其中std::getfor的重载ranges::subrange[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);

效果:相当于:

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只有两个重载,并且非常量左值引用没有相应的重载,这使得我们无法使用非迭代器(godbolt)std::get应用于左值:input_rangecopyable

#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::get只有两个函数重载ranges::subrange呢?它会错过ranges::subrange&and的重载const ranges::subrange&&吗?这是标准缺陷还是故意的?

标签: c++c++20std-ranges

解决方案


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

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

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


推荐阅读