Issue 4027: possibly-const-range should prefer returning const R& (original) (raw)


This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of WP status.

4027. _possibly-const-range_ should prefer returning const R&

Section: 25.2 [ranges.syn] Status: WP Submitter: Hewill Kang Opened: 2023-12-17 Last modified: 2024-11-28

Priority: 2

View all other issues in [ranges.syn].

View all issues with WP status.

Discussion:

_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

int main() { auto r = std::views::single(0) | std::views::transform( { 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::cbeginspecifies 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 Ror 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 Ris 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

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_typewhose reference type is const int& and satisfies constant_range.

[2024-03-11; Reflector poll]

Set priority to 2 after reflector poll. Send to SG9.

[St. Louis 2024-06-28; LWG and SG9 joint session: move to Ready]

[Wrocław 2024-11-23; Status changed: Voting → WP.]

Proposed resolution:

This wording is relative to N4971.

  1. Modify 25.2 [ranges.syn], header <ranges> synopsis, as indicated:

    #include // see 17.12.1 [compare.syn]
    #include // see 17.11.2 [initializer.list.syn]
    #include // see 24.2 [iterator.synopsis]

    namespace std::ranges {
    […]

    // 25.7.22 [range.as.const], as const view
    template
    constexpr auto& possibly-const-range(R& r) noexcept { // exposition only
    if constexpr (inputconstant_range && !constant_range) {

   return const_cast<const R&>(r);  
 } else {  
   return r;  
 }  

}

[…]
}