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

4003. view_interface::back is overconstrained

Section: 25.5.3 [view.interface] Status: Tentatively NAD Submitter: Hewill Kang Opened: 2023-10-28 Last modified: 2024-06-24

Priority: Not Prioritized

View all other issues in [view.interface].

View all issues with Tentatively NAD status.

Discussion:

Currently, view_interface only provides the back member when the derived class satisfies both bidirectional_range and common_range, which ensures that ranges::prev can act its sentinel.

However, requiring common_range seems to be too strict because when the derived class satisfies both random_access_range and sized_range, its end iterator can still be calculated in constant time, which is what some range adaptors currently do to greedily become common ranges.

I think we should follow similar rules to eliminate this inconsistency (demo):

#include <ranges>

constexpr auto r = std::ranges::subrange(std::views::iota(0), 5);
constexpr auto z = std::views::zip(r);
static_assert(r.back() == 4); // ill-formed
static_assert(std::get<0>(z.back()) == 4); // ok

[2023-11-07; Reflector poll]

NAD. "During the concat discussion LEWG decided not to support the corner case of random-access sized but not-common ranges." "If we did want to address such ranges, would be better to enforce commonness for random-access sized ranges by having ranges::end return ranges::begin(r) + ranges::size(r)."

Proposed resolution:

This wording is relative to N4964.

  1. Modify 25.5.3 [view.interface], class template view_interface synopsis, as indicated:

    namespace std::ranges {
      template<class D>
        requires is_class_v<D> && same_as<D, remove_cv_t<D>>
      class view_interface {
        […]
      public:
        […]
        constexpr decltype(auto) back() requires (bidirectional_range<D> && common_range<D>) ||
                                                 (random_access_range<D> && sized_range<D>);
        constexpr decltype(auto) back() const
          requires (bidirectional_range<const D> && common_range<const D>) ||
                   (random_access_range<const D> && sized_range<const D>);
        […]
      };
    }
    
  2. Modify 25.5.3.2 [view.interface.members] as indicated:

    constexpr decltype(auto) back() requires (bidirectional_range<D> && common_range<D>) ||
                                             (random_access_range<D> && sized_range<D>);
    constexpr decltype(auto) back() const
      requires (bidirectional_range<const D> && common_range<const D>) ||
               (random_access_range<const D> && sized_range<const D>);
    

    -3- Preconditions: !empty() is true.

    -4- Effects: Equivalent to:

    auto common-arg-end = []<class R>(R& r) {
      if constexpr (common_range<R>) {
        return ranges::end(r);
      } else {
        return ranges::begin(r) + ranges::distance(r);
      }
    };
    return *ranges::prev(common-arg-endranges::end(derived()));