Title
basic_const_iterator should have custom iter_move
Status
c++23
Section
[const.iterators]
Submitter
Hewill Kang

Created on 2023-01-31.00:00:00 last changed 13 months ago

Messages

Date: 2023-02-13.11:31:32

Proposed resolution:

This wording is relative to N4928.

  1. Modify [const.iterators] as indicated:

    namespace std {
      template<class I>
        concept not-a-const-iterator = see below;
    
      template<indirectly_readable I>
        using iter-const-rvalue-reference-t =  // exposition only
          common_reference_t<const iter_value_t<I>&&, iter_rvalue_reference_t<I>>;
    
      template<input_iterator Iterator>
      class basic_const_iterator {
        Iterator current_ = Iterator();                             // exposition only
        using reference = iter_const_reference_t<Iterator>;         // exposition only
        using rvalue-reference = iter-const-rvalue-reference-t<Iterator>;  // exposition only
    
      public:
        […]
        template<sized_sentinel_for<Iterator> S>
          requires different-from<S, basic_const_iterator>
          friend constexpr difference_type operator-(const S& x, const basic_const_iterator& y);
          friend constexpr rvalue-reference iter_move(const basic_const_iterator& i)
            noexcept(noexcept(static_cast<rvalue-reference>(ranges::iter_move(i.current_)))) 
          {
            return static_cast<rvalue-reference>(ranges::iter_move(i.current_));
          }
      };
    }
    
Date: 2023-02-13.00:00:00

[ 2023-02-13 Approved at February 2023 meeting in Issaquah. Status changed: Immediate → WP. ]

Date: 2023-02-10.17:52:26

[ Issaquah 2023-02-10; LWG issue processing ]

Move to Immediate for C++23

Date: 2023-01-31.00:00:00

The standard does not currently customize iter_move for basic_const_iterator, which means that applying iter_move to basic_const_iterator will invoke the default behavior. Although the intent of such an operation is unpredictable, it does introduce some inconsistencies:

int x[] = {1, 2, 3};
using R1 = decltype(           x  | views::as_rvalue | views::as_const);
using R2 = decltype(           x  | views::as_const  | views::as_rvalue);
using Z1 = decltype(views::zip(x) | views::as_rvalue | views::as_const);
using Z2 = decltype(views::zip(x) | views::as_const  | views::as_rvalue);

static_assert(same_as<ranges::range_reference_t<R1>,       const int&&>);
static_assert(same_as<ranges::range_reference_t<R2>,       const int&&>);
static_assert(same_as<ranges::range_reference_t<Z1>, tuple<const int&&>>);
static_assert(same_as<ranges::range_reference_t<Z2>, tuple<const int&&>>); // failed

In the above example, views::zip(x) | views::as_const will produce a range whose iterator type is basic_const_iterator with reference of tuple<const int&>. Since iter_move adopts the default behavior, its rvalue reference will also be tuple<const int&>, so applying views::as_rvalue to it won't have any effect.

Such an inconsistency seems undesirable.

The proposed resolution adds an iter_move specialization for basic_const_iterator and specifies the return type as common_reference_t<const iter_value_t<It>&&, iter_rvalue_reference_t<It>>, which is the type that input_iterator is guaranteed to be valid. This is also in sync with the behavior of range-v3.

History
Date User Action Args
2023-11-22 15:47:43adminsetstatus: wp -> c++23
2023-02-13 11:31:32adminsetmessages: + msg13399
2023-02-13 11:31:32adminsetstatus: immediate -> wp
2023-02-10 17:52:26adminsetmessages: + msg13338
2023-02-10 17:52:26adminsetstatus: new -> immediate
2023-02-05 12:57:05adminsetmessages: + msg13257
2023-01-31 00:00:00admincreate