Created on 2023-12-17.00:00:00 last changed 3 weeks ago
Proposed resolution:
This wording is relative to N4971.
Modify [ranges.syn], header <ranges> synopsis, as indicated:
#include <compare> // see [compare.syn] #include <initializer_list> // see [initializer.list.syn] #include <iterator> // see [iterator.synopsis] namespace std::ranges { […] // [range.as.const], as const view template<input_range R> constexpr auto& possibly-const-range(R& r) noexcept { // exposition only if constexpr (inputconstant_range<const R>&& !constant_range<R>) { return const_cast<const R&>(r); } else { return r; } } […] }
[ Wrocław 2024-11-23; Status changed: Voting → WP. ]
[ St. Louis 2024-06-28; LWG and SG9 joint session: move to Ready ]
[ 2024-03-11; Reflector poll ]
Set priority to 2 after reflector poll. Send to SG9.
possibly-const-range currently only returns const R& when R does not satisfy constant_range and const R satisfies constant_range.
Although it's not clear why we need the former condition, this does diverge from the legacy std::cbegin (demo):
#include <ranges>
int main() {
auto r = std::views::single(0)
| std::views::transform([](int) { return 0; });
using C1 = decltype(std::ranges::cbegin(r));
using C2 = decltype(std::cbegin(r));
static_assert(std::same_as<C1, C2>); // failed
}
Since R itself is constant_range, so possibly-const-range, above just returns R& and C1 is transform_view::iterator<false>; std::cbegin specifies to return as_const(r).begin(), which makes that C2 is transform_view::iterator<true> which is different from C1.
I believe const R& should always be returned if it's a range, regardless of whether const R or R is a constant_range, just as fmt-maybe-const in format ranges always prefers const R over R. Although it is theoretically possible for R to satisfy constant_range and that const R is a mutable range, such nonsense range type should not be of interest. This relaxation of constraints allows for maximum consistency with std::cbegin, and in some cases can preserve constness to the greatest extent (demo):
#include <ranges>
int main() {
auto r = std::views::single(0) | std::views::lazy_split(0);
(*std::ranges::cbegin(r)).front() = 42; // ok
(*std::cbegin(r)).front() = 42; // not ok
}
Above, *std::ranges::cbegin returns a range of type const lazy_split_view::outer-iterator<false>::value_type, which does not satisfy constant_range because its reference type is int&.
However, *std::cbegin(r) returns lazy_split_view::outer-iterator<true>::value_type whose reference type is const int& and satisfies constant_range.History | |||
---|---|---|---|
Date | User | Action | Args |
2024-11-28 21:40:31 | admin | set | messages: + msg14475 |
2024-11-28 21:40:31 | admin | set | status: voting -> wp |
2024-11-19 16:09:07 | admin | set | status: ready -> voting |
2024-06-28 20:32:30 | admin | set | messages: + msg14221 |
2024-06-28 20:32:30 | admin | set | status: open -> ready |
2024-03-11 22:01:29 | admin | set | messages: + msg13987 |
2024-03-11 22:01:29 | admin | set | status: new -> open |
2023-12-22 11:29:13 | admin | set | messages: + msg13896 |
2023-12-17 00:00:00 | admin | create |