Title
Hardening `view_interface::operator[]`
Status
open
Section
[view.interface]
Submitter
Hewill Kang

Created on 2026-05-11.00:00:00 last changed 1 week ago

Messages

Date: 2026-06-17.15:33:08

Proposed resolution:

This wording is relative to N5046.

  1. Modify [version.syn], Header <version> synopsis, as indicated:

    #define __cpp_lib_hardened_vector 202502L // also in <vector>
    #define __cpp_lib_hardened_view_interface 202506YYYYMML // also in <ranges>
    
  2. 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:
        […]
        template<random_access_range R = D>
          constexpr decltype(auto) operator[](range_difference_t<R> n) {
            return ranges::begin(derived())[n];
          };
        template<random_access_range R = const D>
          constexpr decltype(auto) operator[](range_difference_t<R> n) const {
            return ranges::begin(derived())[n];
          };
      };
    }
    
  3. Modify [view.interface.members] as indicated:

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

    -3- Hardened preconditions: `!empty()` is `true`.

    -4- Effects: Equivalent to: return *ranges::prev(ranges::end(derived()));

    
    template<random_access_range R = D>
      constexpr decltype(auto) operator[](range_difference_t<R> n);
    template<random_access_range R = const D>
      constexpr decltype(auto) operator[](range_difference_t<R> n) const;
    

    -?- Hardened preconditions: If the implementation defines `__cpp_lib_hardened_view_interface` and `D` models `sized_range`, then n >= 0 && n < ranges::distance(derived()) is `true`.

    -?- Effects: Equivalent to: return ranges::begin(derived())[n];

Date: 2026-06-15.00:00:00

[ 2026-06-17; Change status Ready → Open; Jiang An comments and provides improved wording ]

Consider the following program:

#include <ranges>

int main() 
{
  int a[2]{};
  std::ranges::subrange<int*> sr{a, a + 1};
  return sr[1]; // out of the bound of sr, but in the bound of the underlying range
}

Currently, the program has well-defined behavior, because the validity of the `operator[]` call is merely based on the reachable range of the stored iterator.

LWG 4577 introduces a hardened precondition for the `operator[]`, which permits implementations to perform bound check and terminate the program if the index is incorrect for the subrange. However, the current (2026-06-03) proposed resolution will break some programs even on non-hardened implementations, by introducing UB that's not expected to be detected.

Perhaps we should restrict the precondition to hardened implementations, or some "partially hardened" implementations (which are not yet conforming though) hardening `view_interface`.

Date: 2026-06-17.15:33:08

[ Brno 2026-06-08; Change status Open → Ready. ]

This wording is relative to N5046.

  1. Modify [version.syn], Header <version> synopsis, as indicated:

    #define __cpp_lib_hardened_vector 202502L // also in <vector>
    #define __cpp_lib_hardened_view_interface 202506YYYYMML // also in <ranges>
    
  2. 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:
        […]
        template<random_access_range R = D>
          constexpr decltype(auto) operator[](range_difference_t<R> n) {
            return ranges::begin(derived())[n];
          };
        template<random_access_range R = const D>
          constexpr decltype(auto) operator[](range_difference_t<R> n) const {
            return ranges::begin(derived())[n];
          };
      };
    }
    
  3. Modify [view.interface.members] as indicated:

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

    -3- Hardened preconditions: `!empty()` is `true`.

    -4- Effects: Equivalent to: return *ranges::prev(ranges::end(derived()));

    
    template<random_access_range R = D>
      constexpr decltype(auto) operator[](range_difference_t<R> n);
    template<random_access_range R = const D>
      constexpr decltype(auto) operator[](range_difference_t<R> n) const;
    

    -?- Hardened preconditions: If `D` models `sized_range`, then n >= 0 && n < ranges::distance(derived()) is `true`.

    -?- Effects: Equivalent to: return ranges::begin(derived())[n];

Date: 2026-06-15.00:00:00

[ 2026-06-03; Jonathan provides new wording ]

Date: 2026-06-15.00:00:00

[ 2026-06-03; status: Ready → Open ]

Reopen to bump the feature test macro.

This wording is relative to N5046.

  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:
        […]
        template<random_access_range R = D>
          constexpr decltype(auto) operator[](range_difference_t<R> n) {
            return ranges::begin(derived())[n];
          };
        template<random_access_range R = const D>
          constexpr decltype(auto) operator[](range_difference_t<R> n) const {
            return ranges::begin(derived())[n];
          };
      };
    }
    
  2. Modify [view.interface.members] as indicated:

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

    -3- Hardened preconditions: `!empty()` is `true`.

    -4- Effects: Equivalent to: return *ranges::prev(ranges::end(derived()));

    
    template<random_access_range R = D>
      constexpr decltype(auto) operator[](range_difference_t<R> n);
    template<random_access_range R = const D>
      constexpr decltype(auto) operator[](range_difference_t<R> n) const;
    

    -?- Hardened preconditions: If `D` models `sized_range`, then n >= 0 && n < ranges::distance(derived()) is `true`.

    -?- Effects: Equivalent to: return ranges::begin(derived())[n];

Date: 2026-05-29.00:00:00

[ 2026-05-29 LWG telecon; status: New → Ready ]

Date: 2026-05-11.00:00:00

Hardening `view_interface::operator[]` to prevent out-of-bounds access is worthwhile for the `sized_range` cases, and we have already done so for other standard views such as `span` and `string_view`'s `operator[]`.

History
Date User Action Args
2026-06-17 15:33:08adminsetmessages: + msg16485
2026-06-17 15:33:08adminsetstatus: ready -> open
2026-06-09 08:16:17adminsetmessages: + msg16373
2026-06-09 08:16:17adminsetstatus: open -> ready
2026-06-04 20:52:13adminsetstatus: ready -> open
2026-06-03 12:01:17adminsetmessages: + msg16362
2026-06-03 12:01:17adminsetmessages: + msg16361
2026-05-30 15:28:42adminsetmessages: + msg16360
2026-05-30 15:28:42adminsetstatus: new -> ready
2026-05-16 12:27:04adminsetmessages: + msg16295
2026-05-11 00:00:00admincreate