reverse_iterator/common_iterator's operator-> should not require the underlying iterator's operator-> to be a const member function
Hewill Kang

Created on 2022-06-27.00:00:00 last changed 1 month ago


Date: 2022-07-03.11:24:34

Proposed resolution:

This wording is relative to N4910.

  1. Modify [reverse.iter.elem] as indicated:

    constexpr pointer operator->() const
      requires (is_pointer_v<Iterator> ||
                requires(const Iterator i) { i.operator->(); });

    -2- Effects:

    1. (2.1) — If Iterator is a pointer type, equivalent to: return prev(current);

    2. (2.2) — Otherwise, equivalent to: return prev(current).operator->();

  2. Modify [common.iter.access] as indicated:

    constexpr decltype(auto) operator->() const
      requires see below;

    -3- The expression in the requires-clause is equivalent to:

      indirectly_readable<const I> &&
      (requires(const I& i) { i.operator->(); } ||
      is_reference_v<iter_reference_t<I>> ||
      constructible_from<iter_value_t<I>, iter_reference_t<I>>)

    -4- Preconditions: holds_alternative<I>(v_) is true.

    -5- Effects:

    1. (5.1) — If I is a pointer type or if the expression get<I>(v_).operator->() is well-formedrequires(I i) { i.operator->(); } is true, equivalent to: return get<I>(v_);

    2. (5.2) — Otherwise, if iter_reference_t<I> is a reference type, equivalent to:

        auto&& tmp = *get<I>(v_);
        return addressof(tmp);
    3. (5.3) — Otherwise, equivalent to: return proxy(*get<I>(v_)); where proxy is the exposition-only class:

        class proxy {
          iter_value_t<I> keep_;
          constexpr proxy(iter_reference_t<I>&& x)
            : keep_(std::move(x)) {}
          constexpr const iter_value_t<I>* operator->() const noexcept {
            return addressof(keep_);


Date: 2022-06-27.00:00:00

For non-pointer types, reverse_iterator::operator-> requires that the Iterator must have an operator->() with const-qualifier, whereas in the Effects clause, it always invokes the non-const object's operator->().

common_iterator::operator-> also requires that I::operator->() must be const-qualified, which seems reasonable since the return type of get<I>(v_) is const I&. However, LWG 3672 makes common_iterator::operator->() always return a value, which makes it unnecessary to detect the constness of I::operator->(), because it will be invoked with a non-const returned object anyway.

I think we should remove this constraint as I don't see the benefit of doing that. Constraining iterator's operator->() to be const and finally invoking non-const overload doesn't feel right to me either. In <ranges>, the exposition-only constraint has-arrow ([range.utility.helpers]) for operator->() does not require that the underlying iterator's operator->() to be const, we should make them consistent, and I believe this relaxation of constraints can bring some value.


This issue's second part of the resolution actually depends on 3672 being applied. But note that the reference wording below is still N4910.

Date User Action Args
2022-07-03 11:24:34adminsetmessages: + msg12544
2022-06-27 00:00:00admincreate