2727#include < __ranges/all.h>
2828#include < __ranges/concepts.h>
2929#include < __ranges/empty.h>
30+ #include < __ranges/helpers.h>
3031#include < __ranges/non_propagating_cache.h>
3132#include < __ranges/range_adaptor.h>
3233#include < __ranges/view_interface.h>
4142
4243_LIBCPP_BEGIN_NAMESPACE_STD
4344
44- // Note: `join_view` is still marked experimental because there is an ABI-breaking change that affects `join_view` in
45- // the pipeline (https://isocpp.org/files/papers/D2770R0.html).
46- // TODO: make `join_view` non-experimental once D2770 is implemented.
47- #if _LIBCPP_STD_VER >= 20 && defined(_LIBCPP_ENABLE_EXPERIMENTAL)
45+ #if _LIBCPP_STD_VER >= 20
4846
4947namespace ranges {
5048 template <class >
@@ -84,11 +82,16 @@ namespace ranges {
8482 template <class >
8583 friend struct std ::__segmented_iterator_traits;
8684
87- static constexpr bool _UseCache = !is_reference_v<_InnerRange>;
88- using _Cache = _If<_UseCache, __non_propagating_cache<remove_cvref_t <_InnerRange>>, __empty_cache>;
89- _LIBCPP_NO_UNIQUE_ADDRESS _Cache __cache_;
9085 _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
9186
87+ static constexpr bool _UseOuterCache = !forward_range<_View>;
88+ using _OuterCache = _If<_UseOuterCache, __non_propagating_cache<iterator_t <_View>>, __empty_cache>;
89+ _LIBCPP_NO_UNIQUE_ADDRESS _OuterCache __outer_;
90+
91+ static constexpr bool _UseInnerCache = !is_reference_v<_InnerRange>;
92+ using _InnerCache = _If<_UseInnerCache, __non_propagating_cache<remove_cvref_t <_InnerRange>>, __empty_cache>;
93+ _LIBCPP_NO_UNIQUE_ADDRESS _InnerCache __inner_;
94+
9295 public:
9396 _LIBCPP_HIDE_FROM_ABI
9497 join_view () requires default_initializable<_View> = default ;
@@ -105,16 +108,22 @@ namespace ranges {
105108
106109 _LIBCPP_HIDE_FROM_ABI
107110 constexpr auto begin () {
108- constexpr bool __use_const = __simple_view<_View> &&
109- is_reference_v<range_reference_t <_View>>;
110- return __iterator<__use_const>{*this , ranges::begin (__base_)};
111+ if constexpr (forward_range<_View>) {
112+ constexpr bool __use_const = __simple_view<_View> &&
113+ is_reference_v<range_reference_t <_View>>;
114+ return __iterator<__use_const>{*this , ranges::begin (__base_)};
115+ } else {
116+ __outer_.__emplace (ranges::begin (__base_));
117+ return __iterator<false >{*this };
118+ }
111119 }
112120
113121 template <class _V2 = _View>
114122 _LIBCPP_HIDE_FROM_ABI
115123 constexpr auto begin () const
116- requires input_range<const _V2> &&
117- is_reference_v<range_reference_t<const _V2>>
124+ requires forward_range<const _V2> &&
125+ is_reference_v<range_reference_t<const _V2>> &&
126+ input_range<range_reference_t<const _V2>>
118127 {
119128 return __iterator<true >{*this , ranges::begin (__base_)};
120129 }
@@ -134,13 +143,12 @@ namespace ranges {
134143 template <class _V2 = _View>
135144 _LIBCPP_HIDE_FROM_ABI
136145 constexpr auto end () const
137- requires input_range<const _V2> &&
138- is_reference_v<range_reference_t<const _V2>>
146+ requires forward_range<const _V2> &&
147+ is_reference_v<range_reference_t<const _V2>> &&
148+ input_range<range_reference_t<const _V2>>
139149 {
140150 using _ConstInnerRange = range_reference_t <const _View>;
141- if constexpr (forward_range<const _View> &&
142- is_reference_v<_ConstInnerRange> &&
143- forward_range<_ConstInnerRange> &&
151+ if constexpr (forward_range<_ConstInnerRange> &&
144152 common_range<const _View> &&
145153 common_range<_ConstInnerRange>) {
146154 return __iterator<true >{*this , ranges::end (__base_)};
@@ -154,11 +162,10 @@ namespace ranges {
154162 requires view<_View> && input_range<range_reference_t <_View>>
155163 template <bool _Const>
156164 struct join_view <_View>::__sentinel {
157- template <bool >
158- friend struct __sentinel ;
159-
160165 private:
161- using _Parent = __maybe_const<_Const, join_view<_View>>;
166+ friend join_view;
167+
168+ using _Parent = __maybe_const<_Const, join_view>;
162169 using _Base = __maybe_const<_Const, _View>;
163170 sentinel_t <_Base> __end_ = sentinel_t <_Base>();
164171
@@ -179,7 +186,7 @@ namespace ranges {
179186 requires sentinel_for<sentinel_t <_Base>, iterator_t <__maybe_const<_OtherConst, _View>>>
180187 _LIBCPP_HIDE_FROM_ABI
181188 friend constexpr bool operator ==(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
182- return __x.__outer_ == __y.__end_ ;
189+ return __x.__get_outer () == __y.__end_ ;
183190 }
184191 };
185192
@@ -191,9 +198,7 @@ namespace ranges {
191198 template <bool _Const>
192199 struct join_view <_View>::__iterator final
193200 : public __join_view_iterator_category<__maybe_const<_Const, _View>> {
194-
195- template <bool >
196- friend struct __iterator ;
201+ friend join_view;
197202
198203 template <class >
199204 friend struct std ::__segmented_iterator_traits;
@@ -209,21 +214,24 @@ namespace ranges {
209214
210215 static constexpr bool __ref_is_glvalue = is_reference_v<range_reference_t <_Base>>;
211216
217+ static constexpr bool _OuterPresent = forward_range<_Base>;
218+ using _OuterType = _If<_OuterPresent, _Outer, __empty_cache>; // TODO Use `__empty` from D154238
219+
212220 public:
213- _Outer __outer_ = _Outer ();
221+ _LIBCPP_NO_UNIQUE_ADDRESS _OuterType __outer_ = _OuterType ();
214222
215223 private:
216224 optional<_Inner> __inner_;
217225 _Parent *__parent_ = nullptr ;
218226
219227 _LIBCPP_HIDE_FROM_ABI
220228 constexpr void __satisfy () {
221- for (; __outer_ != ranges::end (__parent_->__base_ ); ++__outer_ ) {
222- auto && __inner = [& ]() -> auto && {
229+ for (; __get_outer () != ranges::end (__parent_->__base_ ); ++__get_outer () ) {
230+ auto && __inner = [this ]() -> auto && {
223231 if constexpr (__ref_is_glvalue)
224- return *__outer_ ;
232+ return *__get_outer () ;
225233 else
226- return __parent_->__cache_ .__emplace_from ([&]() -> decltype (auto ) { return *__outer_ ; });
234+ return __parent_->__inner_ .__emplace_from ([&]() -> decltype (auto ) { return *__get_outer () ; });
227235 }();
228236 __inner_ = ranges::begin (__inner);
229237 if (*__inner_ != ranges::end (__inner))
@@ -234,8 +242,37 @@ namespace ranges {
234242 __inner_.reset ();
235243 }
236244
245+ _LIBCPP_HIDE_FROM_ABI constexpr _Outer& __get_outer () {
246+ if constexpr (forward_range<_Base>) {
247+ return __outer_;
248+ } else {
249+ return *__parent_->__outer_ ;
250+ }
251+ }
252+
253+ _LIBCPP_HIDE_FROM_ABI constexpr const _Outer& __get_outer () const {
254+ if constexpr (forward_range<_Base>) {
255+ return __outer_;
256+ } else {
257+ return *__parent_->__outer_ ;
258+ }
259+ }
260+
261+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator (_Parent& __parent, _Outer __outer)
262+ requires forward_range<_Base>
263+ : __outer_(std::move(__outer)), __parent_(std::addressof(__parent)) {
264+ __satisfy ();
265+ }
266+
267+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator (_Parent& __parent)
268+ requires(!forward_range<_Base>)
269+ : __parent_(std::addressof(__parent)) {
270+ __satisfy ();
271+ }
272+
237273 _LIBCPP_HIDE_FROM_ABI constexpr __iterator (_Parent* __parent, _Outer __outer, _Inner __inner)
238- : __outer_(std::move(__outer)), __inner_(std::move(__inner)), __parent_(__parent) {}
274+ requires forward_range<_Base>
275+ : __outer_(std::move(__outer)), __inner_(std::move(__inner)), __parent_(__parent) {}
239276
240277 public:
241278 using iterator_concept = _If<
@@ -254,15 +291,7 @@ namespace ranges {
254291 using difference_type = common_type_t <
255292 range_difference_t <_Base>, range_difference_t <range_reference_t <_Base>>>;
256293
257- _LIBCPP_HIDE_FROM_ABI
258- __iterator () requires default_initializable<_Outer> = default ;
259-
260- _LIBCPP_HIDE_FROM_ABI
261- constexpr __iterator (_Parent& __parent, _Outer __outer)
262- : __outer_(std::move(__outer))
263- , __parent_(std::addressof(__parent)) {
264- __satisfy ();
265- }
294+ _LIBCPP_HIDE_FROM_ABI __iterator () = default;
266295
267296 _LIBCPP_HIDE_FROM_ABI
268297 constexpr __iterator (__iterator<!_Const> __i)
@@ -287,14 +316,14 @@ namespace ranges {
287316
288317 _LIBCPP_HIDE_FROM_ABI
289318 constexpr __iterator& operator ++() {
290- auto && __inner = [&]() -> auto && {
319+ auto __get_inner_range = [&]() -> decltype ( auto ) {
291320 if constexpr (__ref_is_glvalue)
292- return *__outer_ ;
321+ return *__get_outer () ;
293322 else
294- return *__parent_->__cache_ ;
295- }() ;
296- if (++*__inner_ == ranges::end (__inner )) {
297- ++__outer_ ;
323+ return *__parent_->__inner_ ;
324+ };
325+ if (++*__inner_ == ranges::end (ranges::__as_lvalue ( __get_inner_range ()) )) {
326+ ++__get_outer () ;
298327 __satisfy ();
299328 }
300329 return *this ;
@@ -324,11 +353,11 @@ namespace ranges {
324353 common_range<range_reference_t <_Base>>
325354 {
326355 if (__outer_ == ranges::end (__parent_->__base_ ))
327- __inner_ = ranges::end (*--__outer_);
356+ __inner_ = ranges::end (ranges::__as_lvalue ( *--__outer_) );
328357
329358 // Skip empty inner ranges when going backwards.
330- while (*__inner_ == ranges::begin (*__outer_)) {
331- __inner_ = ranges::end (*--__outer_);
359+ while (*__inner_ == ranges::begin (ranges::__as_lvalue ( *__outer_) )) {
360+ __inner_ = ranges::end (ranges::__as_lvalue ( *--__outer_) );
332361 }
333362
334363 --*__inner_;
@@ -350,7 +379,7 @@ namespace ranges {
350379 _LIBCPP_HIDE_FROM_ABI
351380 friend constexpr bool operator ==(const __iterator& __x, const __iterator& __y)
352381 requires __ref_is_glvalue &&
353- equality_comparable< iterator_t < _Base> > &&
382+ forward_range< _Base> &&
354383 equality_comparable<iterator_t <range_reference_t <_Base>>>
355384 {
356385 return __x.__outer_ == __y.__outer_ && __x.__inner_ == __y.__inner_ ;
@@ -436,7 +465,7 @@ struct __segmented_iterator_traits<_JoinViewIterator> {
436465 }
437466};
438467
439- #endif // #if _LIBCPP_STD_VER >= 20 && defined(_LIBCPP_ENABLE_EXPERIMENTAL)
468+ #endif // #if _LIBCPP_STD_VER >= 20
440469
441470_LIBCPP_END_NAMESPACE_STD
442471
0 commit comments