Title
elements_view::iterator::operator* missing conditional noexcept specification
Status
new
Section
[range.elements.iterator]
Submitter
Hewill Kang

Created on 2024-07-02.00:00:00 last changed 1 month ago

Messages

Date: 2024-08-02.21:52:03

Proposed resolution:

This wording is relative to N4981.

  1. Modify [range.elements.iterator] as indicated:

    namespace std::ranges {
      template<input_range V, size_t N>
        requires view<V> && has-tuple-element<range_value_t<V>, N> &&
                 has-tuple-element<remove_reference_t<range_reference_t<V>>, N> &&
                 returnable-element<range_reference_t<V>, N>
      template<bool Const>
      class elements_view<V, N>::iterator {
        […]
    
        static constexpr decltype(auto) get-element(const iterator_t<Base>& i);     // exposition only
          noexcept(noexcept(std::get<N>(*i))) requires is_reference_v<range_reference_t<Base>> {
          return std::get<N>(*i);
        }
        
        static constexpr decltype(auto) get-element(const iterator_t<Base>& i)      // exposition only
          noexcept(noexcept(std::get<N>(*i)) &&
                   is_nothrow_move_constructible_v<tuple_element_t<N, range_reference_t<Base>>>) {
          using E = remove_cv_t<tuple_element_t<N, range_reference_t<Base>>>;
          return static_cast<E>(std::get<N>(*i));
        }
    
      public:
        […]
    
        constexpr decltype(auto) operator*() const
          noexcept(noexcept(get-element(current_)))
        { return get-element(current_); }
    
        […]
      };
    }
    

    […]

    static constexpr decltype(auto) get-element(const iterator_t<Base>& i);

    -3- Effects: Equivalent to:

    if constexpr (is_reference_v<range_reference_t<Base>>) {
      return std::get<N>(*i);
    } else {
      using E = remove_cv_t<tuple_element_t<N, range_reference_t<Base>>>;
      return static_cast<E>(std::get<N>(*i));
    }
    
Date: 2024-08-15.00:00:00

[ 2024-08-02; Reflector poll ]

Set priority to 3 after reflector poll.

Date: 2024-07-02.00:00:00

views::elements, which can be seen as a specialization of views::transform, is used to transform the original tuple into its elements.

Since neither has a specialization for iter_move, the customization point is dispatched to the default implementation. However, unlike the latter, elements_view::iterator::operator* does not have a noexcept specification, which makes calls to iter_move never noexcept, which seems to be an oversight (demo):

#include <ranges>
#include <vector>

std::vector v{std::pair{1, "a"}, {2, "b"}};

auto r1 = v | std::views::keys;
auto i1 = r1.begin();
static_assert(noexcept(*i1));                        // failed
static_assert(noexcept(std::ranges::iter_move(i1))); // failed

auto get_key = [](auto& t) noexcept -> auto& { return std::get<0>(t); };
auto r2 = v | std::views::transform(get_key);
auto i2 = r2.begin();
static_assert(noexcept(*i2));
static_assert(noexcept(std::ranges::iter_move(i2)));

The proposed resolution is aligned with the strengthened implementation of MSVC-STL.

History
Date User Action Args
2024-08-02 21:52:03adminsetmessages: + msg14292
2024-07-07 12:34:12adminsetmessages: + msg14229
2024-07-02 00:00:00admincreate