Created on 2020-11-18.00:00:00 last changed 13 months ago
Proposed resolution:
This wording is relative to N4878.
Modify [range.elements.view], as indicated:
namespace std::ranges { template<class T, size_t N> concept has-tuple-element = // exposition only requires(T t) { typename tuple_size<T>::type; requires N < tuple_size_v<T>; typename tuple_element_t<N, T>; { get<N>(t) } -> convertible_to<const tuple_element_t<N, T>&>; }; template<class T, size_t N> concept returnable-element = is_reference_v<T> || move_constructible<tuple_element_t<N, T>>; 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> class elements_view : public view_interface<elements_view<V, N>> { […] }; }
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 { // exposition only using Base = maybe-const<Const, V>; // exposition only iterator_t<Base> current_ = iterator_t<Base>(); // exposition only static constexpr decltype(auto) get-element(const iterator_t<Base>& i); // exposition only public: […] constexpr decltype(auto) operator*() const { returnget<N>get-element(*current_); } […] constexpr decltype(auto) operator[](difference_type n) const requires random_access_range<Base> { returnget<N>get-element(*(current_ + n)); } }; }static constexpr decltype(auto) get-element(const iterator_t<Base>& i); // exposition only-?- Effects: Equivalent to:
if constexpr (is_reference_v<range_reference_t<Base>>) { return get<N>(*i); } else { using E = remove_cv_t<tuple_element_t<N, range_reference_t<Base>>>; return static_cast<E>(get<N>(*i)); }
Modify [range.elements.sentinel] 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>::sentinel { // exposition only […] }; }
[ 2021-02-26 Approved at February 2021 virtual plenary. Status changed: Tentatively Ready → WP. ]
[ 2021-02-08; Reflector poll ]
Set status to Tentatively Ready after six votes in favour during reflector poll.
[ 2021-01-31 Tim adds PR ]
[ 2020-11-29; Reflector prioritization ]
Set priority to 2 during reflector discussions.
This compiles but the resulting view is full of dangling references:
std::vector<int> vec = {42}; auto r = vec | std::views::transform([](auto c) { return std::make_tuple(c, c); }) | std::views::keys;
This is because elements_view::iterator::operator* is specified as
constexpr decltype(auto) operator*() const { return get<N>(*current_); }
Here *current_ is a prvalue, and so the get<N> produces a reference into the materialized temporary that becomes dangling as soon as operator* returns.
We should either ban this case altogether, or make operator* (and operator[]) return by value when *current_ is a prvalue and the corresponding tuple element is not a reference (since this get is std::get, we need not worry about weird user-defined overloads.)History | |||
---|---|---|---|
Date | User | Action | Args |
2023-11-22 15:47:43 | admin | set | status: wp -> c++23 |
2021-02-26 17:31:29 | admin | set | messages: + msg11706 |
2021-02-26 17:31:29 | admin | set | status: ready -> wp |
2021-02-08 13:01:11 | admin | set | messages: + msg11683 |
2021-02-08 13:01:11 | admin | set | status: new -> ready |
2021-01-31 16:45:21 | admin | set | messages: + msg11674 |
2021-01-31 16:45:21 | admin | set | messages: + msg11673 |
2020-11-29 14:06:26 | admin | set | messages: + msg11637 |
2020-11-18 00:00:00 | admin | create |