From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id 757343858D39 for ; Wed, 16 Aug 2023 16:04:43 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 757343858D39 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1692201883; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=IgzZEtq+iJ/prO0vAYaYyH8bQyTI/at0azRU4gmFl6U=; b=Hi08pwrSgHZLeTs4/j1CLO4BBJL18I4L2vL9REmA191yKgzelywMbv1ROavce5xFyemhWT kPwZ8lMlRMAaLNyOpG2bMF9AfI3hfasJZC87vZg5XPojwl/lVHpSQqSvGTyAu5aKQDjclu 6u/cZJzg4tOmGpayLmF3wIUj3n5jfdI= Received: from mail-lj1-f199.google.com (mail-lj1-f199.google.com [209.85.208.199]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-48-BkIkEvcHNuixFv-AJOlWzg-1; Wed, 16 Aug 2023 12:04:40 -0400 X-MC-Unique: BkIkEvcHNuixFv-AJOlWzg-1 Received: by mail-lj1-f199.google.com with SMTP id 38308e7fff4ca-2bb930719ebso17309201fa.3 for ; Wed, 16 Aug 2023 09:04:40 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1692201879; x=1692806679; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=IgzZEtq+iJ/prO0vAYaYyH8bQyTI/at0azRU4gmFl6U=; b=AZxWyhtYlpTglIsAQPJU6uzDaYPAOJhVXkz4m5ntZQd2l7xNMuCulwcaNdH9M/ik89 hj3U8yQthNtgPuUwkt6UmMY4A8fGRolK8mAHnKthefqkmxtXj34qP5UIOZC291LtWHV3 IudH3+gUp0Lb7WJ5fPKWi9olWK86yWKf57Lq2kh61mJWK0PyX/vUbqS9I14j4HPf3ToQ 3FagEhmcprVEj5pTrByBZ56jPTMr3AdSGje7YucSthae7ovTAHB/kiEy8NhbSapUQAjb ojOYF4yNFz2Scf2eMuZLh3e6N9upK4iOIFqghcUC5G396Xnx1RYOiXh8CWh3pzgOe3UQ 2FMA== X-Gm-Message-State: AOJu0Yzb6pvxUszRCX2tmM2LDvk7NPT/ps5AMjXbpiYrzgLglVs4sIVC /i+6Jum4EpuWecPJxB1NRkbQIeX7PLmpIUos68SEK5JktTWsls8Q/9nknvpr1ZQgG0OR46XASSt ur7oBNN7Y0CuFcYTv7u/CSfQGBPFAEISSz2JJRb0= X-Received: by 2002:a2e:988c:0:b0:2b6:e625:ba55 with SMTP id b12-20020a2e988c000000b002b6e625ba55mr1964838ljj.41.1692201879039; Wed, 16 Aug 2023 09:04:39 -0700 (PDT) X-Google-Smtp-Source: AGHT+IF7ijAQTORz9OI6mpLJJeFcA8NeSC1QE4ZQuM3Ol9OeGU1VT1jckDRkEva011xgBcfgzH2y1nAOg+If7k9spWk= X-Received: by 2002:a2e:988c:0:b0:2b6:e625:ba55 with SMTP id b12-20020a2e988c000000b002b6e625ba55mr1964802ljj.41.1692201878422; Wed, 16 Aug 2023 09:04:38 -0700 (PDT) MIME-Version: 1.0 References: <20230417133916.3110637-1-ppalka@redhat.com> In-Reply-To: <20230417133916.3110637-1-ppalka@redhat.com> From: Patrick Palka Date: Wed, 16 Aug 2023 12:04:27 -0400 Message-ID: Subject: Re: [PATCH] libstdc++: Implement P2770R0 changes to join_view / join_with_view To: gcc-patches@gcc.gnu.org Cc: libstdc++@gcc.gnu.org X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable X-Spam-Status: No, score=-13.7 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,GIT_PATCH_0,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H4,RCVD_IN_MSPIKE_WL,SPF_HELO_NONE,SPF_NONE,TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: On Mon, Apr 17, 2023 at 9:39=E2=80=AFAM Patrick Palka w= rote: > > This C++23 paper fixes a bug in these views when adapting a certain kind > of non-forward range, and we treat it as a DR against C++20. > > Tested on x86_64-pc-linux-gnu, does this look OK for GCC 13? This > is an ABI change for join_view so it'd be unsuitable for backporting > later I think :( Ping, does this look OK for trunk? > > libstdc++-v3/ChangeLog: > > * include/bits/regex.h (regex_iterator::iterator_concept): > Define for C++20 as per P2770R0. > (regex_token_iterator::iterator_concept): Likewise. > * include/std/ranges (__detail::__as_lvalue): Define. > (join_view::_Iterator): Befriend join_view. > (join_view::_Iterator::_M_satisfy): Use _M_get_outer > instead of _M_outer. > (join_view::_Iterator::_M_get_outer): Define. > (join_view::_Iterator::_Iterator): Split constructor taking > _Parent argument into two as per P2770R0. Remove constraint on > default constructor. > (join_view::_Iterator::_M_outer): Make this data member present > only when the underlying range is forward. > (join_view::_Iterator::operator++): Use _M_get_outer instead of > _M_outer. > (join_view::_Iterator::operator--): Use __as_lvalue helper. > (join_view::_Iterator::operator=3D=3D): Adjust constraints as per > P2770R0. > (join_view::_Sentinel::__equal): Use _M_get_outer instead of > _M_outer. > (join_view::_M_outer): New data member when the underlying range > is non-forward. > (join_view::begin): Adjust definition as per P2770R0. > (join_view::end): Likewise. > (join_with_view::_M_outer_it): New data member when the > underlying range is non-forward. > (join_with_view::begin): Adjust definition as per P2770R0. > (join_with_view::end): Likewise. > (join_with_view::_Iterator::_M_outer_it): Make this data member > present only when the underlying range is forward. > (join_with_view::_Iterator::_M_get_outer): Define. > (join_with_view::_Iterator::_Iterator): Split constructor > taking _Parent argument into two as per P2770R0. Remove > constraint on default constructor. > (join_with_view::_Iterator::_M_update_inner): Adjust definition > as per P2770R0. > (join_with_view::_Iterator::_M_get_inner): Likewise. > (join_with_view::_Iterator::_M_satisfy): Adjust calls to > _M_get_inner. Use _M_get_outer instead of _M_outer_it. > (join_with_view::_Iterator::operator=3D=3D): Adjust constraints > as per P2770R0. > (join_with_view::_Sentinel::operator=3D=3D): Use _M_get_outer > instead of _M_outer_it. > * testsuite/std/ranges/adaptors/p2770r0.cc: New test. > --- > libstdc++-v3/include/bits/regex.h | 6 + > libstdc++-v3/include/std/ranges | 190 +++++++++++++----- > .../testsuite/std/ranges/adaptors/p2770r0.cc | 110 ++++++++++ > 3 files changed, 257 insertions(+), 49 deletions(-) > create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/p2770r0.cc > > diff --git a/libstdc++-v3/include/bits/regex.h b/libstdc++-v3/include/bit= s/regex.h > index 26ac6a21c31..2d306868721 100644 > --- a/libstdc++-v3/include/bits/regex.h > +++ b/libstdc++-v3/include/bits/regex.h > @@ -2740,6 +2740,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 > typedef const value_type* pointer; > typedef const value_type& reference; > typedef std::forward_iterator_tag iterator_category; > +#if __cplusplus > 201703L > + typedef std::input_iterator_tag iterator_concept; > +#endif > > /** > * @brief Provides a singular iterator, useful for indicating > @@ -2869,6 +2872,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 > typedef const value_type* pointer; > typedef const value_type& reference; > typedef std::forward_iterator_tag iterator_category= ; > +#if __cplusplus > 201703L > + typedef std::input_iterator_tag iterator_concept; > +#endif > > public: > /** > diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/r= anges > index 283d757faa4..ddcf50cc93e 100644 > --- a/libstdc++-v3/include/std/ranges > +++ b/libstdc++-v3/include/std/ranges > @@ -2705,6 +2705,14 @@ namespace views::__adaptor > inline constexpr _DropWhile drop_while; > } // namespace views > > + namespace __detail > + { > + template > + constexpr _Tp& > + __as_lvalue(_Tp&& __t) > + { return static_cast<_Tp&>(__t); } > + } // namespace __detail > + > template > requires view<_Vp> && input_range> > class join_view : public view_interface> > @@ -2767,6 +2775,8 @@ namespace views::__adaptor > using _Parent =3D __detail::__maybe_const_t<_Const, join_view>; > using _Base =3D join_view::_Base<_Const>; > > + friend join_view; > + > static constexpr bool _S_ref_is_glvalue > =3D join_view::_S_ref_is_glvalue<_Const>; > > @@ -2780,9 +2790,10 @@ namespace views::__adaptor > return _M_parent->_M_inner._M_emplace_deref(__x); > }; > > - for (; _M_outer !=3D ranges::end(_M_parent->_M_base); ++_M_ou= ter) > + _Outer_iter& __outer =3D _M_get_outer(); > + for (; __outer !=3D ranges::end(_M_parent->_M_base); ++__oute= r) > { > - auto&& __inner =3D __update_inner(_M_outer); > + auto&& __inner =3D __update_inner(__outer); > _M_inner =3D ranges::begin(__inner); > if (_M_inner !=3D ranges::end(__inner)) > return; > @@ -2811,7 +2822,36 @@ namespace views::__adaptor > using _Outer_iter =3D join_view::_Outer_iter<_Const>; > using _Inner_iter =3D join_view::_Inner_iter<_Const>; > > - _Outer_iter _M_outer =3D _Outer_iter(); > + constexpr _Outer_iter& > + _M_get_outer() > + { > + if constexpr (forward_range<_Base>) > + return _M_outer; > + else > + return *_M_parent->_M_outer; > + } > + > + constexpr const _Outer_iter& > + _M_get_outer() const > + { > + if constexpr (forward_range<_Base>) > + return _M_outer; > + else > + return *_M_parent->_M_outer; > + } > + > + constexpr > + _Iterator(_Parent* __parent, _Outer_iter __outer) requires forw= ard_range<_Base> > + : _M_outer(std::move(__outer)), _M_parent(__parent) > + { _M_satisfy(); } > + > + constexpr explicit > + _Iterator(_Parent* __parent) requires (!forward_range<_Base>) > + : _M_parent(__parent) > + { _M_satisfy(); } > + > + [[no_unique_address]] > + __detail::__maybe_present_t, _Outer_iter= > _M_outer; > optional<_Inner_iter> _M_inner; > _Parent* _M_parent =3D nullptr; > > @@ -2823,13 +2863,7 @@ namespace views::__adaptor > =3D common_type_t, > range_difference_t>>= ; > > - _Iterator() requires default_initializable<_Outer_iter> =3D def= ault; > - > - constexpr > - _Iterator(_Parent* __parent, _Outer_iter __outer) > - : _M_outer(std::move(__outer)), > - _M_parent(__parent) > - { _M_satisfy(); } > + _Iterator() =3D default; > > constexpr > _Iterator(_Iterator __i) > @@ -2857,13 +2891,13 @@ namespace views::__adaptor > { > auto&& __inner_range =3D [this] () -> auto&& { > if constexpr (_S_ref_is_glvalue) > - return *_M_outer; > + return *_M_get_outer(); > else > return *_M_parent->_M_inner; > }(); > if (++*_M_inner =3D=3D ranges::end(__inner_range)) > { > - ++_M_outer; > + ++_M_get_outer(); > _M_satisfy(); > } > return *this; > @@ -2890,9 +2924,9 @@ namespace views::__adaptor > && common_range> > { > if (_M_outer =3D=3D ranges::end(_M_parent->_M_base)) > - _M_inner =3D ranges::end(*--_M_outer); > - while (*_M_inner =3D=3D ranges::begin(*_M_outer)) > - *_M_inner =3D ranges::end(*--_M_outer); > + _M_inner =3D ranges::end(__detail::__as_lvalue(*--_M_outer)= ); > + while (*_M_inner =3D=3D ranges::begin(__detail::__as_lvalue(*= _M_outer))) > + *_M_inner =3D ranges::end(__detail::__as_lvalue(*--_M_outer= )); > --*_M_inner; > return *this; > } > @@ -2911,7 +2945,7 @@ namespace views::__adaptor > friend constexpr bool > operator=3D=3D(const _Iterator& __x, const _Iterator& __y) > requires _S_ref_is_glvalue > - && equality_comparable<_Outer_iter> > + && forward_range<_Base> > && equality_comparable<_Inner_iter> > { > return (__x._M_outer =3D=3D __y._M_outer > @@ -2943,7 +2977,7 @@ namespace views::__adaptor > template > constexpr bool > __equal(const _Iterator<_Const2>& __i) const > - { return __i._M_outer =3D=3D _M_end; } > + { return __i._M_get_outer() =3D=3D _M_end; } > > sentinel_t<_Base> _M_end =3D sentinel_t<_Base>(); > > @@ -2972,6 +3006,9 @@ namespace views::__adaptor > }; > > _Vp _M_base =3D _Vp(); > + [[no_unique_address]] > + __detail::__maybe_present_t, > + __detail::__non_propagating_cache>> _M_outer; > [[no_unique_address]] > __detail::__non_propagating_cache> _M_in= ner; > > @@ -2994,16 +3031,25 @@ namespace views::__adaptor > constexpr auto > begin() > { > - constexpr bool __use_const > - =3D (__detail::__simple_view<_Vp> > - && is_reference_v>); > - return _Iterator<__use_const>{this, ranges::begin(_M_base)}; > + if constexpr (forward_range<_Vp>) > + { > + constexpr bool __use_const > + =3D (__detail::__simple_view<_Vp> > + && is_reference_v>); > + return _Iterator<__use_const>{this, ranges::begin(_M_base)}; > + } > + else > + { > + _M_outer =3D ranges::begin(_M_base); > + return _Iterator{this}; > + } > } > > constexpr auto > begin() const > - requires input_range > + requires forward_range > && is_reference_v> > + && input_range> > { > return _Iterator{this, ranges::begin(_M_base)}; > } > @@ -3022,11 +3068,11 @@ namespace views::__adaptor > > constexpr auto > end() const > - requires input_range > + requires forward_range > && is_reference_v> > + && input_range> > { > - if constexpr (forward_range > - && is_reference_v> > + if constexpr (is_reference_v> > && forward_range> > && common_range > && common_range>) > @@ -6948,6 +6994,9 @@ namespace views::__adaptor > using _InnerRange =3D range_reference_t<_Vp>; > > _Vp _M_base =3D _Vp(); > + [[no_unique_address]] > + __detail::__maybe_present_t, > + __detail::__non_propagating_cache>> _M_outer_it; > __detail::__non_propagating_cache> _M_inner= ; > _Pattern _M_pattern =3D _Pattern(); > > @@ -7035,16 +7084,25 @@ namespace views::__adaptor > constexpr auto > begin() > { > - constexpr bool __use_const =3D is_reference_v<_InnerRange> > - && __detail::__simple_view<_Vp> && __detail::__simple_view<_Patte= rn>; > - return _Iterator<__use_const>{*this, ranges::begin(_M_base)}; > + if constexpr (forward_range<_Vp>) > + { > + constexpr bool __use_const =3D is_reference_v<_InnerRange> > + && __detail::__simple_view<_Vp> && __detail::__simple_view<_P= attern>; > + return _Iterator<__use_const>{*this, ranges::begin(_M_base)}; > + } > + else > + { > + _M_outer_it =3D ranges::begin(_M_base); > + return _Iterator{*this}; > + } > } > > constexpr auto > begin() const > - requires input_range > + requires forward_range > && forward_range > && is_reference_v> > + && input_range> > { return _Iterator{*this, ranges::begin(_M_base)}; } > > constexpr auto > @@ -7062,13 +7120,13 @@ namespace views::__adaptor > > constexpr auto > end() const > - requires input_range > + requires forward_range > && forward_range > && is_reference_v> > + && input_range> > { > using _InnerConstRange =3D range_reference_t; > - if constexpr (forward_range > - && forward_range<_InnerConstRange> > + if constexpr (forward_range<_InnerConstRange> > && common_range > && common_range<_InnerConstRange>) > return _Iterator{*this, ranges::end(_M_base)}; > @@ -7105,35 +7163,69 @@ namespace views::__adaptor > static constexpr bool _S_ref_is_glvalue =3D join_with_view::_S_ref_i= s_glvalue<_Const>; > > _Parent* _M_parent =3D nullptr; > - _OuterIter _M_outer_it =3D _OuterIter(); > + [[no_unique_address]] > + __detail::__maybe_present_t, _OuterIter> _M_o= uter_it; > variant<_PatternIter, _InnerIter> _M_inner_it; > > + constexpr _OuterIter& > + _M_get_outer() > + { > + if constexpr (forward_range<_Base>) > + return _M_outer_it; > + else > + return *_M_parent->_M_outer_it; > + } > + > + constexpr const _OuterIter& > + _M_get_outer() const > + { > + if constexpr (forward_range<_Base>) > + return _M_outer_it; > + else > + return *_M_parent->_M_outer_it; > + } > + > constexpr > - _Iterator(_Parent& __parent, iterator_t<_Base> __outer) > + _Iterator(_Parent& __parent, _OuterIter __outer) > + requires forward_range<_Base> > : _M_parent(std::__addressof(__parent)), _M_outer_it(std::move(__out= er)) > { > - if (_M_outer_it !=3D ranges::end(_M_parent->_M_base)) > + if (_M_get_outer() !=3D ranges::end(_M_parent->_M_base)) > + { > + auto&& __inner =3D _M_update_inner(); > + _M_inner_it.template emplace<1>(ranges::begin(__inner)); > + _M_satisfy(); > + } > + } > + > + constexpr > + _Iterator(_Parent& __parent) > + requires (!forward_range<_Base>) > + : _M_parent(std::__addressof(__parent)) > + { > + if (_M_get_outer() !=3D ranges::end(_M_parent->_M_base)) > { > - auto&& __inner =3D _M_update_inner(_M_outer_it); > + auto&& __inner =3D _M_update_inner(); > _M_inner_it.template emplace<1>(ranges::begin(__inner)); > _M_satisfy(); > } > } > > - constexpr auto&& > - _M_update_inner(const _OuterIter& __x) > + constexpr auto& > + _M_update_inner() > { > + _OuterIter& __outer =3D _M_get_outer(); > if constexpr (_S_ref_is_glvalue) > - return *__x; > + return __detail::__as_lvalue(*__outer); > else > - return _M_parent->_M_inner._M_emplace_deref(__x); > + return _M_parent->_M_inner._M_emplace_deref(__outer); > } > > - constexpr auto&& > - _M_get_inner(const _OuterIter& __x) > + constexpr auto& > + _M_get_inner() > { > if constexpr (_S_ref_is_glvalue) > - return *__x; > + return __detail::__as_lvalue(*_M_get_outer()); > else > return *_M_parent->_M_inner; > } > @@ -7148,16 +7240,16 @@ namespace views::__adaptor > if (std::get<0>(_M_inner_it) !=3D ranges::end(_M_parent->_M= _pattern)) > break; > > - auto&& __inner =3D _M_update_inner(_M_outer_it); > + auto&& __inner =3D _M_update_inner(); > _M_inner_it.template emplace<1>(ranges::begin(__inner)); > } > else > { > - auto&& __inner =3D _M_get_inner(_M_outer_it); > + auto&& __inner =3D _M_get_inner(); > if (std::get<1>(_M_inner_it) !=3D ranges::end(__inner)) > break; > > - if (++_M_outer_it =3D=3D ranges::end(_M_parent->_M_base)) > + if (++_M_get_outer() =3D=3D ranges::end(_M_parent->_M_base)= ) > { > if constexpr (_S_ref_is_glvalue) > _M_inner_it.template emplace<0>(); > @@ -7196,7 +7288,7 @@ namespace views::__adaptor > iter_difference_t<_InnerIter>, > iter_difference_t<_PatternIter>= >; > > - _Iterator() requires default_initializable<_OuterIter> =3D default; > + _Iterator() =3D default; > > constexpr > _Iterator(_Iterator __i) > @@ -7306,7 +7398,7 @@ namespace views::__adaptor > friend constexpr bool > operator=3D=3D(const _Iterator& __x, const _Iterator& __y) > requires _S_ref_is_glvalue > - && equality_comparable<_OuterIter> && equality_comparable<_InnerI= ter> > + && forward_range<_Base> && equality_comparable<_InnerIter> > { return __x._M_outer_it =3D=3D __y._M_outer_it && __x._M_inner_it = =3D=3D__y._M_inner_it; } > > friend constexpr common_reference_t, > @@ -7373,7 +7465,7 @@ namespace views::__adaptor > iterator_t<__detail::__maybe_const_t<_OtherCo= nst, _Vp>>> > friend constexpr bool > operator=3D=3D(const _Iterator<_OtherConst>& __x, const _Sentinel& _= _y) > - { return __x._M_outer_it =3D=3D __y._M_end; } > + { return __x._M_get_outer() =3D=3D __y._M_end; } > }; > > namespace views > diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/p2770r0.cc b/libs= tdc++-v3/testsuite/std/ranges/adaptors/p2770r0.cc > new file mode 100644 > index 00000000000..15d71b2faa9 > --- /dev/null > +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/p2770r0.cc > @@ -0,0 +1,110 @@ > +// { dg-options "-std=3Dgnu++20" } > +// { dg-do run { target c++20 } } > + > +#include > +#include > +#include > +#include > +#include > + > +namespace ranges =3D std::ranges; > +namespace views =3D std::views; > + > +void > +test01() > +{ > + // Test case from LWG 3698 > + char const text[] =3D "Hello"; > + std::regex regex{"[a-z]"}; > + > + auto lower > + =3D ranges::subrange(std::cregex_iterator(ranges::begin(text), > + ranges::end(text), > + regex), > + std::cregex_iterator{}) > + | views::join > + | views::transform([](auto const& sm) { > + return std::string_view(sm.first, sm.second); > + }); > + > + VERIFY( ranges::equal(lower, (std::string_view[]){"e", "l", "l", "o"})= ); > +} > + > +void > +test02() > +{ > +#if __cpp_lib_ranges_join_with > + // Analogous test case from LWG 3698 for join_with_view > + char const text[] =3D "Hello"; > + std::regex regex{"[a-z]"}; > + > + auto lower > + =3D ranges::subrange(std::cregex_iterator(ranges::begin(text), > + ranges::end(text), > + regex), > + std::cregex_iterator{}) > + | views::join_with(views::empty>) > + | views::transform([](auto const& sm) { > + return std::string_view(sm.first, sm.second); > + }); > + > + VERIFY( ranges::equal(lower, (std::string_view[]){"e", "l", "l", "o"})= ); > +#endif > +} > + > +void > +test03() > +{ > + // Test case from LWG 3700 > + auto r =3D views::iota(0, 5) | views::split(1); > + auto s =3D views::single(r); > + auto j =3D s | views::join; > + auto f =3D j.front(); > +} > + > +void > +test04() > +{ > +#if __cpp_lib_ranges_join_with > + // Analogous test case from LWG 3700 for join_with_view > + auto r =3D views::iota(0, 5) | views::split(1); > + auto s =3D views::single(r); > + auto j =3D s | views::join_with(views::empty>); > + auto f =3D j.front(); > +#endif > +} > + > +void > +test05() > +{ > + // Test case from LWG 3791 > + std::vector> v =3D {{1}}; > + auto r =3D v > + | views::transform([](auto& x) -> auto&& { return std::move(x); }) > + | views::join; > + auto e =3D --r.end(); > +} > + > +void > +test06() > +{ > +#if __cpp_lib_ranges_join_with > + // Analogous test case from LWG 3791 for join_with_view > + std::vector> v =3D {{1}}; > + auto r =3D v > + | views::transform([](auto& x) -> auto&& { return std::move(x); }) > + | views::join_with(views::empty); > + auto e =3D --r.end(); > +#endif > +} > + > +int > +main() > +{ > + test01(); > + test02(); > + test03(); > + test04(); > + test05(); > + test06(); > +} > -- > 2.40.0.335.g9857273be0 >