Title
view_interface::back is overconstrained
Status
nad
Section
[view.interface]
Submitter
Hewill Kang

Created on 2023-10-28.00:00:00 last changed 13 months ago

Messages

Date: 2023-11-07.18:35:04

Proposed resolution:

This wording is relative to N4964.

  1. Modify [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 [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()));
    
Date: 2023-11-15.00:00:00

[ 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)."

Date: 2023-10-28.00:00:00

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
History
Date User Action Args
2023-11-07 18:35:04adminsetmessages: + msg13823
2023-11-07 18:35:04adminsetstatus: new -> nad
2023-10-28 16:14:08adminsetmessages: + msg13777
2023-10-28 00:00:00admincreate