elements_view::iterator::operator* missing conditional noexcept specificationSection: 25.7.23.3 [range.elements.iterator] Status: New Submitter: Hewill Kang Opened: 2024-07-02 Last modified: 2024-08-02
Priority: 3
View other active issues in [range.elements.iterator].
View all other issues in [range.elements.iterator].
View all issues with New status.
Discussion:
views::elements, which can be seen as a specialization of views::transform,
is used to transform the original tuple into its elements.
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.
[2024-08-02; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
This wording is relative to N4981.
Modify 25.7.23.3 [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)); }