Created on 2021-06-16.00:00:00 last changed 12 months ago
Proposed resolution:
This wording is relative to N4892.
Modify [range.join.iterator] as indicated:
[…]namespace std::ranges { template<input_range V> requires view<V> && input_range<range_reference_t<V>> && (is_reference_v<range_reference_t<V>> || view<range_value_t<V>>) template<bool Const> struct join_view<V>::iterator { […] optional<InnerIter> inner_= InnerIter(); […] iterator() requires default_initializable<OuterIter>&& default_initializable<InnerIter>= default; […] constexpr decltype(auto) operator*() const { return **inner_; } […] friend constexpr decltype(auto) iter_move(const iterator& i) noexcept(noexcept(ranges::iter_move(*i.inner_))) { return ranges::iter_move(*i.inner_); } friend constexpr void iter_swap(const iterator& x, const iterator& y) noexcept(noexcept(ranges::iter_swap(*x.inner_, *y.inner_))) requires indirectly_swappable<InnerIter>; }; }constexpr void satisfy(); // exposition only[…]-5- Effects: Equivalent to:
auto update_inner = [this](const iterator_t<Base>& x) -> auto&& { […] }; for (; outer_ != ranges::end(parent_->base_); ++outer_) { auto&& inner = update_inner(*outer_); inner_ = ranges::begin(inner); if (*inner_ != ranges::end(inner)) return; } if constexpr (ref-is-glvalue) inner_.reset()= InnerIter();constexpr InnerIter operator->() const requires has-arrow<InnerIter> && copyable<InnerIter>;-8- Effects: Equivalent to: return *inner_;
constexpr iterator& operator++();[…]-9- Let inner-range be:
[…] -10- Effects: Equivalent to:auto&& inner_rng = inner-range; if (++*inner_ == ranges::end(inner_rng)) { ++outer_; satisfy(); } return *this;constexpr iterator& operator--() requires ref-is-glvalue && bidirectional_range<Base> && bidirectional_range<range_reference_t<Base>> && common_range<range_reference_t<Base>>;[…]-13- Effects: Equivalent to:
if (outer_ == ranges::end(parent_->base_)) inner_ = ranges::end(*--outer_); while (*inner_ == ranges::begin(*outer_)) *inner_ = ranges::end(*--outer_); --*inner_; return *this;friend constexpr void iter_swap(const iterator& x, const iterator& y) noexcept(noexcept(ranges::iter_swap(*x.inner_, *y.inner_))) requires indirectly_swappable<InnerIter>;-16- Effects: Equivalent to: return ranges::iter_swap(*x.inner_, *y.inner_);
[ 2022-11-12 Approved at November 2022 meeting in Kona. Status changed: Immediate → WP. ]
[ Kona 2022-11-08; Accepted at joint LWG/SG9 session. Move to Immediate ]
[ 2021-08-23; Louis Dionne comments and provides improved wording ]
I believe the currently proposed resolution is missing the removal of the default_initializable<InnerIter> constraint on join_view::iterator's default constructor in [range.join.iterator]. Indeed, after the currently-proposed resolution, join_view::iterator reads like:
template<input_range V> requires […] struct join_view<V>::iterator { private: optional<InnerIter> inner_; // exposition only […] public: iterator() requires default_initializable<OuterIter> && default_initializable<InnerIter> = default; […] };
I believe we should drop the default_initializable<InnerIter> constraint from the default constructor (that seems like an oversight unless I missed something):
template<input_range V> requires […] struct join_view<V>::iterator { private: optional<InnerIter> inner_; // exposition only […] public: iterator() requires default_initializable<OuterIter> = default; […] };
[ 2021-06-23; Reflector poll ]
Set priority to 3 after reflector poll.
Previous resolution [SUPERSEDED]:
Wording relative to the post 2021-06 virtual plenary working draft. This PR is currently being implemented in MSVC.
Modify [range.join.iterator] as indicated:
[…]namespace std::ranges { template<input_range V> requires view<V> && input_range<range_reference_t<V>> && (is_reference_v<range_reference_t<V>> || view<range_value_t<V>>) template<bool Const> struct join_view<V>::iterator { […] optional<InnerIter> inner_= InnerIter(); […] constexpr decltype(auto) operator*() const { return **inner_; } […] friend constexpr decltype(auto) iter_move(const iterator& i) noexcept(noexcept(ranges::iter_move(*i.inner_))) { return ranges::iter_move(*i.inner_); } friend constexpr void iter_swap(const iterator& x, const iterator& y) noexcept(noexcept(ranges::iter_swap(*x.inner_, *y.inner_))) requires indirectly_swappable<InnerIter>; }; }constexpr void satisfy(); // exposition only[…]-5- Effects: Equivalent to:
auto update_inner = [this](const iterator_t<Base>& x) -> auto&& { […] }; for (; outer_ != ranges::end(parent_->base_); ++outer_) { auto&& inner = update_inner(*outer_); inner_ = ranges::begin(inner); if (*inner_ != ranges::end(inner)) return; } if constexpr (ref-is-glvalue) inner_.reset()= InnerIter();constexpr InnerIter operator->() const requires has-arrow<InnerIter> && copyable<InnerIter>;-8- Effects: Equivalent to: return *inner_;
constexpr iterator& operator++();[…]-9- Let inner-range be:
[…] -10- Effects: Equivalent to:auto&& inner_rng = inner-range; if (++*inner_ == ranges::end(inner_rng)) { ++outer_; satisfy(); } return *this;constexpr iterator& operator--() requires ref-is-glvalue && bidirectional_range<Base> && bidirectional_range<range_reference_t<Base>> && common_range<range_reference_t<Base>>;[…]-13- Effects: Equivalent to:
if (outer_ == ranges::end(parent_->base_)) inner_ = ranges::end(*--outer_); while (*inner_ == ranges::begin(*outer_)) *inner_ = ranges::end(*--outer_); --*inner_; return *this;friend constexpr void iter_swap(const iterator& x, const iterator& y) noexcept(noexcept(ranges::iter_swap(*x.inner_, *y.inner_))) requires indirectly_swappable<InnerIter>;-16- Effects: Equivalent to: return ranges::iter_swap(*x.inner_, *y.inner_);
join_view::iterator has exposition-only members outer_ — which holds an iterator into the adapted range — and inner_ — which holds an iterator into the range denoted by outer_. After application of P2325R3 "Views should not be required to be default constructible" to the working draft, single-pass iterators can be non-default_initializable. P2325R3 constrains join_view::iterator's default constructor to require that the types of both outer_ and inner_ are default_initializable, indicating an intent to support such iterator types. However, the effect of the non-default constructor specified in [range.join.iterator] paragraph 6 is to default-initialize inner_, which is ill-formed if its type is not default_initializable.
History | |||
---|---|---|---|
Date | User | Action | Args |
2023-11-22 15:47:43 | admin | set | status: wp -> c++23 |
2022-11-17 00:42:33 | admin | set | messages: + msg13088 |
2022-11-17 00:42:33 | admin | set | status: immediate -> wp |
2022-11-11 02:39:56 | admin | set | status: ready -> immediate |
2022-11-10 23:33:20 | admin | set | messages: + msg13000 |
2022-11-10 23:33:20 | admin | set | status: new -> ready |
2021-08-27 18:24:12 | admin | set | messages: + msg12019 |
2021-06-23 14:18:24 | admin | set | messages: + msg11968 |
2021-06-19 14:33:00 | admin | set | messages: + msg11938 |
2021-06-16 00:00:00 | admin | create |