libstdc++: Implement P2415R2 changes to viewable_range / views::all
This implements the wording changes in P2415R2 "What is a view?", which is a DR for C++20. libstdc++-v3/ChangeLog: * include/bits/ranges_base.h (__detail::__is_initializer_list): Define. (viewable_range): Adjust as per P2415R2. * include/bits/ranges_cmp.h (__cpp_lib_ranges): Adjust value. * 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. * include/std/version (__cpp_lib_ranges): Adjust value. * 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.
This commit is contained in:
parent
bc91cb8d8c
commit
5e1b17f038
7 changed files with 159 additions and 35 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
|||
|
||||
#ifdef __cpp_lib_concepts
|
||||
// Define this here, included by all the headers that need to define it.
|
||||
#define __cpp_lib_ranges 202106L
|
||||
#define __cpp_lib_ranges 202110L
|
||||
|
||||
namespace 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;
|
||||
|
|
|
@ -273,7 +273,7 @@
|
|||
#define __cpp_lib_optional 202106L
|
||||
#define __cpp_lib_polymorphic_allocator 201902L
|
||||
#if __cpp_lib_concepts
|
||||
# define __cpp_lib_ranges 202106L
|
||||
# define __cpp_lib_ranges 202110L
|
||||
#endif
|
||||
#if __cpp_lib_atomic_wait || _GLIBCXX_HAVE_POSIX_SEMAPHORE
|
||||
# define __cpp_lib_semaphore 201907L
|
||||
|
|
|
@ -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:
|
||||
static_assert(noexcept(views::all(std::array<int, 3>{})));
|
||||
struct A { A(); A(const A&); };
|
||||
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.
|
||||
// In particular, 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());
|
||||
}
|
||||
|
|
|
@ -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 now well-formed after P2415R2 relaxed
|
||||
// the requirements of viewable_range to admit 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); });
|
||||
|
|
|
@ -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 now well-formed after P2415R2 relaxed
|
||||
// the requirements of viewable_range to admit 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); });
|
||||
|
|
Loading…
Add table
Reference in a new issue