#c #c 20 #std-ranges
Вопрос:
В стандарте существует четыре парных типа, а именно std::array
, std::pair
, std::tuple
, и ranges::subrange
, где перегрузка std::get
для ranges::subrange
определена в [диапазон.поддиапазон#доступ-10]:
template<size_t N, class I, class S, subrange_kind K> requires (N < 2) constexpr auto get(const subrange<I, S, K>amp; r); template<size_t N, class I, class S, subrange_kind K> requires (N < 2) constexpr auto get(subrange<I, S, K>amp;amp; 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
имеет только две перегрузки, и нет соответствующей перегрузки для ссылки на значение, не являющееся постоянным, что делает невозможным для нас обращаться 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::subrangeamp;
и const ranges::subrangeamp;amp;
? Это стандартный дефект или он преднамеренный?
Ответ №1:
Как правило, полностью сформированный subrange
в четко определенном коде представляет допустимый диапазон, и если в нем хранится размер, то размер равен размеру диапазона. Это отражено в предварительном условии каждого конструктора, не используемого по умолчанию (созданное по умолчанию состояние все еще может быть частично сформировано). Это немного запуталось из-за введения итераторов только для перемещения (поскольку неконстант begin
должен переместить итератор), но остается целью проекта.
Другими словами, subrange
совсем не похоже pair
, tuple
, или array
. Эти три являются совокупностями значений без семантики, и их get
перегрузки отражают это, прозрачно распространяя квалификацию cv и категорию ценности. subrange
с другой стороны, у него есть семантика — это не просто пара итератора и стража. Это get
только для чтения, никогда для письма. Вот почему get
при subrange
возврате по значению.
Не имеет большого смысла предоставлять get
перегрузку без значения const; о возврате по изменяемой ссылке не может быть и речи; возврат по ссылке на const не похож на все остальное (и также удивителен). Возврат по значению означает, что в единственном случае это имеет значение, get
на lvalue subrange
это разрушительная операция, которая была бы совершенно неожиданной.
Комментарии:
1. Другой вопрос, должны ли мы вводить дополнительные ограничения для первой перегрузки, чтобы устранить ошибку «отброшенный классификатор констант», возникающую внутри тела функции? Я планирую подать заявку на LWG для этого, но я не знаю, стоит ли это того.
2. Я не вижу причин запрещать
get<1>
больше, чем.end()
. Это совершенно безвредно и может быть полезно. Однако я вижу некоторую ценность в проверке, которуюbegin
можно вызвать в ограничении.