On Mon, 21 Feb 2022 at 19:39, Patrick Palka via Libstdc++ < libstd...@gcc.gnu.org> wrote:
> Tested on x86_64-pc-linux-gnu, does this look OK for trunk? > OK, thanks. > > libstdc++-v3/ChangeLog: > > * include/bits/ranges_base.h (__detail::__is_initializer_list): > Define. > (viewable_range): Adjust as per P2415R2. > * include/std/ranges (owning_view): Define as per P2415R2. > (enable_borrowed_range<owning_view>): Likewise. > (views::__detail::__can_subrange): Replace with ... > (views::__detail::__can_owning_view): ... this. > (views::_All::_S_noexcept): Sync with operator(). > (views::_All::operator()): Use owning_view instead of subrange > as per P2415R2. > * testsuite/std/ranges/adaptors/all.cc (test06): Adjust now that > views::all uses owning_view instead of subrange. > (test08): New test. > * testsuite/std/ranges/adaptors/lazy_split.cc (test09): Adjust > now that rvalue non-view non-borrowed ranges are viewable. > * testsuite/std/ranges/adaptors/split.cc (test06): Likewise. > --- > libstdc++-v3/include/bits/ranges_base.h | 16 +++- > libstdc++-v3/include/std/ranges | 89 ++++++++++++++++++- > .../testsuite/std/ranges/adaptors/all.cc | 59 ++++++++---- > .../std/ranges/adaptors/lazy_split.cc | 13 ++- > .../testsuite/std/ranges/adaptors/split.cc | 13 ++- > 5 files changed, 157 insertions(+), 33 deletions(-) > > diff --git a/libstdc++-v3/include/bits/ranges_base.h > b/libstdc++-v3/include/bits/ranges_base.h > index 3c5f4b1790a..38db33fd2ce 100644 > --- a/libstdc++-v3/include/bits/ranges_base.h > +++ b/libstdc++-v3/include/bits/ranges_base.h > @@ -634,7 +634,7 @@ namespace ranges > template<typename _Tp> > concept __is_derived_from_view_interface > = requires (_Tp __t) { __is_derived_from_view_interface_fn(__t, > __t); }; > - } > + } // namespace __detail > > /// [range.view] The ranges::view_base type. > struct view_base { }; > @@ -689,11 +689,23 @@ namespace ranges > concept common_range > = range<_Tp> && same_as<iterator_t<_Tp>, sentinel_t<_Tp>>; > > + namespace __detail > + { > + template<typename _Tp> > + inline constexpr bool __is_initializer_list = false; > + > + template<typename _Tp> > + inline constexpr bool __is_initializer_list<initializer_list<_Tp>> > = true; > + } // namespace __detail > + > /// A range which can be safely converted to a view. > template<typename _Tp> > concept viewable_range = range<_Tp> > && ((view<remove_cvref_t<_Tp>> && > constructible_from<remove_cvref_t<_Tp>, _Tp>) > - || (!view<remove_cvref_t<_Tp>> && borrowed_range<_Tp>)); > + || (!view<remove_cvref_t<_Tp>> > + && (is_lvalue_reference_v<_Tp> > + || (movable<remove_reference_t<_Tp>> > + && > !__detail::__is_initializer_list<remove_cvref_t<_Tp>>)))); > > // [range.iter.ops] range iterator operations > > diff --git a/libstdc++-v3/include/std/ranges > b/libstdc++-v3/include/std/ranges > index ac85907f129..3e71ecb32b7 100644 > --- a/libstdc++-v3/include/std/ranges > +++ b/libstdc++-v3/include/std/ranges > @@ -1144,6 +1144,87 @@ namespace views::__adaptor > template<typename _Tp> > inline constexpr bool enable_borrowed_range<ref_view<_Tp>> = true; > > + template<range _Range> > + requires movable<_Range> > + && (!__detail::__is_initializer_list<remove_cv_t<_Range>>) > + class owning_view : public view_interface<owning_view<_Range>> > + { > + private: > + _Range _M_r = _Range(); > + > + public: > + owning_view() requires default_initializable<_Range> = default; > + > + constexpr > + owning_view(_Range&& __t) > + noexcept(is_nothrow_move_constructible_v<_Range>) > + : _M_r(std::move(__t)) > + { } > + > + owning_view(owning_view&&) = default; > + owning_view& operator=(owning_view&&) = default; > + > + constexpr _Range& > + base() & noexcept > + { return _M_r; } > + > + constexpr const _Range& > + base() const& noexcept > + { return _M_r; } > + > + constexpr _Range&& > + base() && noexcept > + { return std::move(_M_r); } > + > + constexpr const _Range&& > + base() const&& noexcept > + { return std::move(_M_r); } > + > + constexpr iterator_t<_Range> > + begin() > + { return ranges::begin(_M_r); } > + > + constexpr sentinel_t<_Range> > + end() > + { return ranges::end(_M_r); } > + > + constexpr auto > + begin() const requires range<const _Range> > + { return ranges::begin(_M_r); } > + > + constexpr auto > + end() const requires range<const _Range> > + { return ranges::end(_M_r); } > + > + constexpr bool > + empty() requires requires { ranges::empty(_M_r); } > + { return ranges::empty(_M_r); } > + > + constexpr bool > + empty() const requires requires { ranges::empty(_M_r); } > + { return ranges::empty(_M_r); } > + > + constexpr auto > + size() requires sized_range<_Range> > + { return ranges::size(_M_r); } > + > + constexpr auto > + size() const requires sized_range<const _Range> > + { return ranges::size(_M_r); } > + > + constexpr auto > + data() requires contiguous_range<_Range> > + { return ranges::data(_M_r); } > + > + constexpr auto > + data() const requires contiguous_range<const _Range> > + { return ranges::data(_M_r); } > + }; > + > + template<typename _Tp> > + inline constexpr bool enable_borrowed_range<owning_view<_Tp>> > + = enable_borrowed_range<_Tp>; > + > namespace views > { > namespace __detail > @@ -1152,7 +1233,7 @@ namespace views::__adaptor > concept __can_ref_view = requires { > ref_view{std::declval<_Range>()}; }; > > template<typename _Range> > - concept __can_subrange = requires { > subrange{std::declval<_Range>()}; }; > + concept __can_owning_view = requires { > owning_view{std::declval<_Range>()}; }; > } // namespace __detail > > struct _All : __adaptor::_RangeAdaptorClosure > @@ -1166,13 +1247,13 @@ namespace views::__adaptor > else if constexpr (__detail::__can_ref_view<_Range>) > return true; > else > - return noexcept(subrange{std::declval<_Range>()}); > + return noexcept(owning_view{std::declval<_Range>()}); > } > > template<viewable_range _Range> > requires view<decay_t<_Range>> > || __detail::__can_ref_view<_Range> > - || __detail::__can_subrange<_Range> > + || __detail::__can_owning_view<_Range> > constexpr auto > operator() [[nodiscard]] (_Range&& __r) const > noexcept(_S_noexcept<_Range>()) > @@ -1182,7 +1263,7 @@ namespace views::__adaptor > else if constexpr (__detail::__can_ref_view<_Range>) > return ref_view{std::forward<_Range>(__r)}; > else > - return subrange{std::forward<_Range>(__r)}; > + return owning_view{std::forward<_Range>(__r)}; > } > > static constexpr bool _S_has_simple_call_op = true; > diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/all.cc > b/libstdc++-v3/testsuite/std/ranges/adaptors/all.cc > index c25d972274f..9d4e714b3e9 100644 > --- a/libstdc++-v3/testsuite/std/ranges/adaptors/all.cc > +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/all.cc > @@ -19,7 +19,9 @@ > // { dg-do run { target c++2a } } > > #include <algorithm> > +#include <array> > #include <ranges> > +#include <vector> > #include <testsuite_hooks.h> > #include <testsuite_iterators.h> > > @@ -130,20 +132,6 @@ test05() > static_assert(!requires { 0 | all; }); > } > > -template<bool B1, bool B2> > -struct BorrowedRange > -{ > - int* ptr = nullptr; > - > - BorrowedRange(int (&arr)[3]) noexcept : ptr(arr) { } > - > - int* begin() const noexcept(B1) { return ptr; } > - int* end() const noexcept(B2) { return ptr + 3; } > -}; > - > -template<bool B1, bool B2> > -const bool std::ranges::enable_borrowed_range<BorrowedRange<B1, B2>> = > true; > - > void > test06() > { > @@ -152,11 +140,11 @@ test06() > // Using ref_view: > static_assert(noexcept(views::all(x))); > > - // Using subrange: > - static_assert(noexcept(views::all(BorrowedRange<true, true>(x)))); > - static_assert(!noexcept(views::all(BorrowedRange<true, false>(x)))); > - static_assert(!noexcept(views::all(BorrowedRange<false, true>(x)))); > - static_assert(!noexcept(views::all(BorrowedRange<false, false>(x)))); > + // Using owning_view: > + struct A { A(); A(const A&); }; > + static_assert(noexcept(views::all(std::array<int, 3>{}))); > + static_assert(!std::is_nothrow_move_constructible_v<std::array<A, 3>>); > + static_assert(!noexcept(views::all(std::array<A, 3>{}))); > } > > void > @@ -173,6 +161,38 @@ test07() > static_assert(!ranges::viewable_range<view_t&>); > } > > +constexpr bool > +test08() > +{ > + // Verify P2415R2 "What is a view?" changes > + // Namely, rvalue non-view non-borrowed ranges are now viewable. > + static_assert(ranges::viewable_range<std::vector<int>&&>); > + static_assert(!ranges::viewable_range<const std::vector<int>&&>); > + > + static_assert(ranges::viewable_range<std::initializer_list<int>&>); > + static_assert(ranges::viewable_range<const > std::initializer_list<int>&>); > + static_assert(!ranges::viewable_range<std::initializer_list<int>&&>); > + static_assert(!ranges::viewable_range<const > std::initializer_list<int>&&>); > + > + using type = views::all_t<std::vector<int>&&>; > + using type = ranges::owning_view<std::vector<int>>; > + > + std::same_as<type> auto v = std::vector<int>{{1,2,3}} | views::all; > + > + VERIFY( ranges::equal(v, (int[]){1,2,3}) ); > + VERIFY( ranges::size(v) == 3 ); > + VERIFY( !ranges::empty(v) ); > + VERIFY( ranges::data(v) == &v[0] ); > + > + const auto w = std::move(v); > + VERIFY( ranges::equal(w, (int[]){1,2,3}) ); > + VERIFY( ranges::size(w) == 3 ); > + VERIFY( !ranges::empty(w) ); > + VERIFY( ranges::data(w) == &w[0] ); > + > + return true; > +} > + > int > main() > { > @@ -183,4 +203,5 @@ main() > test05(); > test06(); > test07(); > + static_assert(test08()); > } > diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc > b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc > index a7d3dd6a23e..e0279dc2a4a 100644 > --- a/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc > +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc > @@ -163,10 +163,15 @@ test09() > static_assert(!requires { lazy_split(p)(); }); > static_assert(!requires { s | lazy_split; }); > > - static_assert(!requires { s | lazy_split(p); }); > - static_assert(!requires { lazy_split(p)(s); }); > - static_assert(!requires { s | (lazy_split(p) | views::all); }); > - static_assert(!requires { (lazy_split(p) | views::all)(s); }); > + // Test the case where the closure object is used as an rvalue and > therefore > + // the copy of p is forwarded as an rvalue. > + // This used to be invalid, but is well-formed after P2415R2 relaxed the > + // requirements of viewable_range to permit rvalue non-view non-borrowed > + // ranges such as std::string&&. > + static_assert(requires { s | lazy_split(p); }); > + static_assert(requires { lazy_split(p)(s); }); > + static_assert(requires { s | (lazy_split(p) | views::all); }); > + static_assert(requires { (lazy_split(p) | views::all)(s); }); > > static_assert(requires { s | lazy_split(views::all(p)); }); > static_assert(requires { lazy_split(views::all(p))(s); }); > diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc > b/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc > index 44245f5071a..4f8dddeb410 100644 > --- a/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc > +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc > @@ -145,10 +145,15 @@ test06() > static_assert(!requires { split(p)(); }); > static_assert(!requires { s | split; }); > > - static_assert(!requires { s | split(p); }); > - static_assert(!requires { split(p)(s); }); > - static_assert(!requires { s | (split(p) | views::all); }); > - static_assert(!requires { (split(p) | views::all)(s); }); > + // Test the case where the closure object is used as an rvalue and > therefore > + // the copy of p is forwarded as an rvalue. > + // This used to be invalid, but is well-formed after P2415R2 relaxed the > + // requirements of viewable_range to permit rvalue non-view non-borrowed > + // ranges such as std::string&&. > + static_assert(requires { s | split(p); }); > + static_assert(requires { split(p)(s); }); > + static_assert(requires { s | (split(p) | views::all); }); > + static_assert(requires { (split(p) | views::all)(s); }); > > static_assert(requires { s | split(views::all(p)); }); > static_assert(requires { split(views::all(p))(s); }); > -- > 2.35.1.225.ge2ac9141e6 > >