为什么 std::get 对 ranges::subrange 只有两个函数重载?
Why does std::get only have two function overloads for ranges::subrange?
标准中有四种pair-like类型,分别是std::array
、std::pair
、std::tuple
和ranges::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::get
对 ranges::subrange
只有两个函数重载?它是否遗漏了 ranges::subrange&
和 const ranges::subrange&&
的重载?这是标准缺陷还是故意的?
作为一般规则,定义良好的代码中的完整格式 subrange
表示有效范围,如果它存储大小,则大小等于范围的大小。这反映在每个非默认构造函数的前提条件中(默认构造状态仍然可以部分形成)。由于引入了仅移动迭代器(因为非常量 begin
需要将迭代器移出),这有点混乱,但仍然是设计意图。
换句话说,subrange
与 pair
、tuple
或 array
完全不同。这三个是没有语义的值的聚合,它们的 get
重载通过透明传播 cv 限定和值类别来反映这一点。另一方面,subrange
确实具有语义——它不仅仅是一对迭代器和哨兵。它的 get
只用于阅读,从不用于写作。这就是为什么 get
在 subrange
returns 按值。
提供非常量左值 get
重载没有多大意义;通过可变引用返回是不可能的;通过引用 const 返回不同于其他一切(而且也令人惊讶)。按值返回意味着在唯一情况下它会有所不同,左值 subrange
上的 get
是破坏性操作,这将是非常意外的。
标准中有四种pair-like类型,分别是std::array
、std::pair
、std::tuple
和ranges::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::get
对 ranges::subrange
只有两个函数重载?它是否遗漏了 ranges::subrange&
和 const ranges::subrange&&
的重载?这是标准缺陷还是故意的?
作为一般规则,定义良好的代码中的完整格式 subrange
表示有效范围,如果它存储大小,则大小等于范围的大小。这反映在每个非默认构造函数的前提条件中(默认构造状态仍然可以部分形成)。由于引入了仅移动迭代器(因为非常量 begin
需要将迭代器移出),这有点混乱,但仍然是设计意图。
换句话说,subrange
与 pair
、tuple
或 array
完全不同。这三个是没有语义的值的聚合,它们的 get
重载通过透明传播 cv 限定和值类别来反映这一点。另一方面,subrange
确实具有语义——它不仅仅是一对迭代器和哨兵。它的 get
只用于阅读,从不用于写作。这就是为什么 get
在 subrange
returns 按值。
提供非常量左值 get
重载没有多大意义;通过可变引用返回是不可能的;通过引用 const 返回不同于其他一切(而且也令人惊讶)。按值返回意味着在唯一情况下它会有所不同,左值 subrange
上的 get
是破坏性操作,这将是非常意外的。