标准中有四种pair-like类型,即std::array
, std::pair
, std::tuple
, 和 ranges::subrange
,其中std::get
的过载为 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);
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::get
ranges::subrange
只有两个函数重载?它是否错过了 ranges::subrange&
的重载和 const ranges::subrange&&
?这是标准缺陷还是有意为之?
最佳答案
作为一般规则,一个完整的 subrange
在定义良好的代码中代表一个有效的范围,如果它存储了一个大小,那么这个大小就等于该范围的大小。这反射(reflect)在每个非默认构造函数的前提条件中(默认构造状态仍然可以是部分形成的)。由于引入了仅移动迭代器(因为非常量 begin
需要将迭代器移出),这有点困惑,但仍然是设计意图。
换句话说,subrange
很不一样pair
, tuple
, 或 array
.这三个是没有语义的值的聚合,它们的 get
重载通过透明地传播 cv 限定和值类别来反射(reflect)这一点。 subrange
,另一方面,确实有语义——它不仅仅是一对迭代器和哨兵。它的 get
只为阅读,绝不为写作。这就是为什么get
在 subrange
按值返回。
提供非常量左值没有多大意义get
重载;通过可变引用返回是不可能的;通过引用返回 const 不同于其他一切(也令人惊讶)。按值返回意味着在唯一的情况下它会有所不同,get
在左值 subrange
是一种破坏性的操作,这将是非常出乎意料的。
关于c++ - 为什么 std::get 只有 range::subrange 的两个函数重载?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69077753/