mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-04-07 07:40:25 +08:00
libstdc++: Implement ranges::safe_range for C++20 (P1870R1)
This change replaces the __forwarding_range implementation detail with the ranges::safe_range concept and adds the ranges::enable_safe_range variable template for opt-in in to the concept. It also adjusts the begin/end/rbegin/rend customization point objects to match the new rules for accessing rvalue ranges only when safe to do so. * include/bits/range_access.h (ranges::enable_safe_range): Define. (ranges::begin, ranges::end, ranges::rbegin, ranges::rend): Constrain to only accept types satisfying safe_range and treat argument as an lvalue when calling a member of performing ADL. (ranges::__detail::__range_impl, ranges::__detail::__forwarding_range): Remove. (ranges::range): Adjust definition. (ranges::safe_range): Define. (ranges::iterator_t, ranges::range_difference_t): Reorder definitions to match the synopsis in the working draft. (ranges::disable_sized_range): Remove duplicate definition. * include/experimental/string_view (ranges::enable_safe_range): Add partial specialization for std::experimental::basic_string_view. * include/std/ranges (ranges::viewable_range, ranges::subrange) (ranges::empty_view, ranges::iota_view): Use safe_range. Specialize enable_safe_range. (ranges::safe_iterator_t, ranges::safe_subrange_t): Define. * include/std/span (ranges::enable_safe_range): Add partial specialization for std::span. * include/std/string_view (ranges::enable_safe_range): Likewise for std::basic_string_view. * testsuite/std/ranges/access/begin.cc: Adjust expected results. * testsuite/std/ranges/access/cbegin.cc: Likewise. * testsuite/std/ranges/access/cdata.cc: Likewise. * testsuite/std/ranges/access/cend.cc: Likewise. * testsuite/std/ranges/access/crbegin.cc: Likewise. * testsuite/std/ranges/access/crend.cc: Likewise. * testsuite/std/ranges/access/data.cc: Likewise. * testsuite/std/ranges/access/end.cc: Likewise. * testsuite/std/ranges/access/rbegin.cc: Likewise. * testsuite/std/ranges/access/rend.cc: Likewise. * testsuite/std/ranges/empty_view.cc: Test ranges::begin and ranges::end instead of unqualified calls to begin and end. * testsuite/std/ranges/safe_range.cc: New test. * testsuite/std/ranges/safe_range_types.cc: New test. * testsuite/util/testsuite_iterators.h: Add comment about safe_range. From-SVN: r279135
This commit is contained in:
parent
43c2de7a57
commit
b5b2e3879d
@ -1,5 +1,42 @@
|
||||
2019-12-09 Jonathan Wakely <jwakely@redhat.com>
|
||||
|
||||
* include/bits/range_access.h (ranges::enable_safe_range): Define.
|
||||
(ranges::begin, ranges::end, ranges::rbegin, ranges::rend): Constrain
|
||||
to only accept types satisfying safe_range and treat argument as an
|
||||
lvalue when calling a member of performing ADL.
|
||||
(ranges::__detail::__range_impl, ranges::__detail::__forwarding_range):
|
||||
Remove.
|
||||
(ranges::range): Adjust definition.
|
||||
(ranges::safe_range): Define.
|
||||
(ranges::iterator_t, ranges::range_difference_t): Reorder definitions
|
||||
to match the synopsis in the working draft.
|
||||
(ranges::disable_sized_range): Remove duplicate definition.
|
||||
* include/experimental/string_view (ranges::enable_safe_range): Add
|
||||
partial specialization for std::experimental::basic_string_view.
|
||||
* include/std/ranges (ranges::viewable_range, ranges::subrange)
|
||||
(ranges::empty_view, ranges::iota_view): Use safe_range. Specialize
|
||||
enable_safe_range.
|
||||
(ranges::safe_iterator_t, ranges::safe_subrange_t): Define.
|
||||
* include/std/span (ranges::enable_safe_range): Add partial
|
||||
specialization for std::span.
|
||||
* include/std/string_view (ranges::enable_safe_range): Likewise for
|
||||
std::basic_string_view.
|
||||
* testsuite/std/ranges/access/begin.cc: Adjust expected results.
|
||||
* testsuite/std/ranges/access/cbegin.cc: Likewise.
|
||||
* testsuite/std/ranges/access/cdata.cc: Likewise.
|
||||
* testsuite/std/ranges/access/cend.cc: Likewise.
|
||||
* testsuite/std/ranges/access/crbegin.cc: Likewise.
|
||||
* testsuite/std/ranges/access/crend.cc: Likewise.
|
||||
* testsuite/std/ranges/access/data.cc: Likewise.
|
||||
* testsuite/std/ranges/access/end.cc: Likewise.
|
||||
* testsuite/std/ranges/access/rbegin.cc: Likewise.
|
||||
* testsuite/std/ranges/access/rend.cc: Likewise.
|
||||
* testsuite/std/ranges/empty_view.cc: Test ranges::begin and
|
||||
ranges::end instead of unqualified calls to begin and end.
|
||||
* testsuite/std/ranges/safe_range.cc: New test.
|
||||
* testsuite/std/ranges/safe_range_types.cc: New test.
|
||||
* testsuite/util/testsuite_iterators.h: Add comment about safe_range.
|
||||
|
||||
* testsuite/27_io/filesystem/path/concat/strings.cc: Test more cases.
|
||||
|
||||
PR libstdc++/92853
|
||||
|
@ -342,6 +342,9 @@ namespace ranges
|
||||
template<typename>
|
||||
inline constexpr bool disable_sized_range = false;
|
||||
|
||||
template<typename _Tp>
|
||||
inline constexpr bool enable_safe_range = false;
|
||||
|
||||
namespace __detail
|
||||
{
|
||||
using __max_diff_type = long long;
|
||||
@ -359,10 +362,19 @@ namespace ranges
|
||||
constexpr make_unsigned_t<_Tp>
|
||||
__to_unsigned_like(_Tp __t) noexcept
|
||||
{ return __t; }
|
||||
|
||||
// Part of the constraints of ranges::safe_range
|
||||
template<typename _Tp>
|
||||
concept __maybe_safe_range
|
||||
= is_lvalue_reference_v<_Tp> || enable_safe_range<remove_cvref_t<_Tp>>;
|
||||
|
||||
} // namespace __detail
|
||||
|
||||
namespace __cust_access
|
||||
{
|
||||
using std::ranges::__detail::__maybe_safe_range;
|
||||
using std::__detail::__class_or_enum;
|
||||
|
||||
template<typename _Tp>
|
||||
constexpr decay_t<_Tp>
|
||||
__decay_copy(_Tp&& __t)
|
||||
@ -370,20 +382,19 @@ namespace ranges
|
||||
{ return std::forward<_Tp>(__t); }
|
||||
|
||||
template<typename _Tp>
|
||||
concept __member_begin = is_lvalue_reference_v<_Tp>
|
||||
&& requires(_Tp __t)
|
||||
{ { __decay_copy(__t.begin()) } -> input_or_output_iterator; };
|
||||
concept __member_begin = requires(_Tp& __t)
|
||||
{
|
||||
{ __decay_copy(__t.begin()) } -> input_or_output_iterator;
|
||||
};
|
||||
|
||||
template<typename _Tp> void begin(_Tp&&) = delete;
|
||||
template<typename _Tp> void begin(initializer_list<_Tp>&&) = delete;
|
||||
|
||||
template<typename _Tp>
|
||||
concept __adl_begin
|
||||
= std::__detail::__class_or_enum<remove_reference_t<_Tp>>
|
||||
&& requires(_Tp&& __t)
|
||||
concept __adl_begin = __class_or_enum<remove_reference_t<_Tp>>
|
||||
&& requires(_Tp& __t)
|
||||
{
|
||||
{ __decay_copy(begin(std::forward<_Tp>(__t))) }
|
||||
-> input_or_output_iterator;
|
||||
{ __decay_copy(begin(__t)) } -> input_or_output_iterator;
|
||||
};
|
||||
|
||||
struct _Begin
|
||||
@ -396,47 +407,45 @@ namespace ranges
|
||||
if constexpr (is_array_v<remove_reference_t<_Tp>>)
|
||||
return true;
|
||||
else if constexpr (__member_begin<_Tp>)
|
||||
return noexcept(__decay_copy(std::declval<_Tp>().begin()));
|
||||
return noexcept(__decay_copy(std::declval<_Tp&>().begin()));
|
||||
else
|
||||
return noexcept(__decay_copy(begin(std::declval<_Tp>())));
|
||||
return noexcept(__decay_copy(begin(std::declval<_Tp&>())));
|
||||
}
|
||||
|
||||
public:
|
||||
template<typename _Tp>
|
||||
template<__maybe_safe_range _Tp>
|
||||
requires is_array_v<remove_reference_t<_Tp>> || __member_begin<_Tp>
|
||||
|| __adl_begin<_Tp>
|
||||
constexpr auto
|
||||
operator()(_Tp&& __e) const noexcept(_S_noexcept<_Tp>())
|
||||
operator()(_Tp&& __t) const noexcept(_S_noexcept<_Tp>())
|
||||
{
|
||||
if constexpr (is_array_v<remove_reference_t<_Tp>>)
|
||||
{
|
||||
static_assert(is_lvalue_reference_v<_Tp>);
|
||||
return __e;
|
||||
return __t;
|
||||
}
|
||||
else if constexpr (__member_begin<_Tp>)
|
||||
return __e.begin();
|
||||
return __t.begin();
|
||||
else
|
||||
return begin(std::forward<_Tp>(__e));
|
||||
return begin(__t);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename _Tp>
|
||||
concept __member_end = is_lvalue_reference_v<_Tp>
|
||||
&& requires(_Tp __t)
|
||||
concept __member_end = requires(_Tp& __t)
|
||||
{
|
||||
{ __decay_copy(__t.end()) }
|
||||
-> sentinel_for<decltype(_Begin{}(__t))>;
|
||||
-> sentinel_for<decltype(_Begin{}(std::forward<_Tp>(__t)))>;
|
||||
};
|
||||
|
||||
template<typename _Tp> void end(_Tp&&) = delete;
|
||||
template<typename _Tp> void end(initializer_list<_Tp>&&) = delete;
|
||||
|
||||
template<typename _Tp>
|
||||
concept __adl_end
|
||||
= std::__detail::__class_or_enum<remove_reference_t<_Tp>>
|
||||
&& requires(_Tp&& __t)
|
||||
concept __adl_end = __class_or_enum<remove_reference_t<_Tp>>
|
||||
&& requires(_Tp& __t)
|
||||
{
|
||||
{ __decay_copy(end(std::forward<_Tp>(__t))) }
|
||||
{ __decay_copy(end(__t)) }
|
||||
-> sentinel_for<decltype(_Begin{}(std::forward<_Tp>(__t)))>;
|
||||
};
|
||||
|
||||
@ -450,28 +459,28 @@ namespace ranges
|
||||
if constexpr (is_array_v<remove_reference_t<_Tp>>)
|
||||
return true;
|
||||
else if constexpr (__member_end<_Tp>)
|
||||
return noexcept(__decay_copy(std::declval<_Tp>().end()));
|
||||
return noexcept(__decay_copy(std::declval<_Tp&>().end()));
|
||||
else
|
||||
return noexcept(__decay_copy(end(std::declval<_Tp>())));
|
||||
return noexcept(__decay_copy(end(std::declval<_Tp&>())));
|
||||
}
|
||||
|
||||
public:
|
||||
template<typename _Tp>
|
||||
template<__maybe_safe_range _Tp>
|
||||
requires is_array_v<remove_reference_t<_Tp>> || __member_end<_Tp>
|
||||
|| __adl_end<_Tp>
|
||||
constexpr auto
|
||||
operator()(_Tp&& __e) const noexcept(_S_noexcept<_Tp>())
|
||||
operator()(_Tp&& __t) const noexcept(_S_noexcept<_Tp>())
|
||||
{
|
||||
if constexpr (is_array_v<remove_reference_t<_Tp>>)
|
||||
{
|
||||
static_assert(is_lvalue_reference_v<_Tp>);
|
||||
static_assert(is_bounded_array_v<remove_reference_t<_Tp>>);
|
||||
return __e + extent_v<remove_reference_t<_Tp>>;
|
||||
return __t + extent_v<remove_reference_t<_Tp>>;
|
||||
}
|
||||
else if constexpr (__member_end<_Tp>)
|
||||
return __e.end();
|
||||
return __t.end();
|
||||
else
|
||||
return end(std::forward<_Tp>(__e));
|
||||
return end(__t);
|
||||
}
|
||||
};
|
||||
|
||||
@ -510,27 +519,25 @@ namespace ranges
|
||||
};
|
||||
|
||||
template<typename _Tp>
|
||||
concept __member_rbegin = is_lvalue_reference_v<_Tp>
|
||||
&& requires(_Tp __t)
|
||||
{ { __decay_copy(__t.rbegin()) } -> input_or_output_iterator; };
|
||||
concept __member_rbegin = requires(_Tp& __t)
|
||||
{
|
||||
{ __decay_copy(__t.rbegin()) } -> input_or_output_iterator;
|
||||
};
|
||||
|
||||
template<typename _Tp> void rbegin(_Tp&&) = delete;
|
||||
|
||||
template<typename _Tp>
|
||||
concept __adl_rbegin
|
||||
= std::__detail::__class_or_enum<remove_reference_t<_Tp>>
|
||||
&& requires(_Tp&& __t)
|
||||
concept __adl_rbegin = __class_or_enum<remove_reference_t<_Tp>>
|
||||
&& requires(_Tp& __t)
|
||||
{
|
||||
{ __decay_copy(rbegin(std::forward<_Tp>(__t))) }
|
||||
-> input_or_output_iterator;
|
||||
{ __decay_copy(rbegin(__t)) } -> input_or_output_iterator;
|
||||
};
|
||||
|
||||
template<typename _Tp>
|
||||
concept __reversable = requires(_Tp&& __t)
|
||||
concept __reversable = requires(_Tp& __t)
|
||||
{
|
||||
{ _Begin{}(std::forward<_Tp>(__t)) } -> bidirectional_iterator;
|
||||
{ _End{}(std::forward<_Tp>(__t)) }
|
||||
-> same_as<decltype(_Begin{}(std::forward<_Tp>(__t)))>;
|
||||
{ _Begin{}(__t) } -> bidirectional_iterator;
|
||||
{ _End{}(__t) } -> same_as<decltype(_Begin{}(__t))>;
|
||||
};
|
||||
|
||||
struct _RBegin
|
||||
@ -541,14 +548,14 @@ namespace ranges
|
||||
_S_noexcept()
|
||||
{
|
||||
if constexpr (__member_rbegin<_Tp>)
|
||||
return noexcept(__decay_copy(std::declval<_Tp>().rbegin()));
|
||||
return noexcept(__decay_copy(std::declval<_Tp&>().rbegin()));
|
||||
else if constexpr (__adl_rbegin<_Tp>)
|
||||
return noexcept(__decay_copy(rbegin(std::declval<_Tp>())));
|
||||
return noexcept(__decay_copy(rbegin(std::declval<_Tp&>())));
|
||||
else
|
||||
{
|
||||
if constexpr (noexcept(_End{}(std::declval<_Tp>())))
|
||||
if constexpr (noexcept(_End{}(std::declval<_Tp&>())))
|
||||
{
|
||||
using _It = decltype(_End{}(std::declval<_Tp>()));
|
||||
using _It = decltype(_End{}(std::declval<_Tp&>()));
|
||||
// std::reverse_iterator copy-initializes its member.
|
||||
return is_nothrow_copy_constructible_v<_It>;
|
||||
}
|
||||
@ -558,24 +565,23 @@ namespace ranges
|
||||
}
|
||||
|
||||
public:
|
||||
template<typename _Tp>
|
||||
template<__maybe_safe_range _Tp>
|
||||
requires __member_rbegin<_Tp> || __adl_rbegin<_Tp> || __reversable<_Tp>
|
||||
constexpr auto
|
||||
operator()(_Tp&& __e) const
|
||||
operator()(_Tp&& __t) const
|
||||
noexcept(_S_noexcept<_Tp>())
|
||||
{
|
||||
if constexpr (__member_rbegin<_Tp>)
|
||||
return __e.rbegin();
|
||||
return __t.rbegin();
|
||||
else if constexpr (__adl_rbegin<_Tp>)
|
||||
return rbegin(std::forward<_Tp>(__e));
|
||||
return rbegin(__t);
|
||||
else
|
||||
return std::make_reverse_iterator(_End{}(std::forward<_Tp>(__e)));
|
||||
return std::make_reverse_iterator(_End{}(__t));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename _Tp>
|
||||
concept __member_rend = is_lvalue_reference_v<_Tp>
|
||||
&& requires(_Tp __t)
|
||||
concept __member_rend = requires(_Tp& __t)
|
||||
{
|
||||
{ __decay_copy(__t.rend()) }
|
||||
-> sentinel_for<decltype(_RBegin{}(__t))>;
|
||||
@ -584,11 +590,10 @@ namespace ranges
|
||||
template<typename _Tp> void rend(_Tp&&) = delete;
|
||||
|
||||
template<typename _Tp>
|
||||
concept __adl_rend
|
||||
= std::__detail::__class_or_enum<remove_reference_t<_Tp>>
|
||||
&& requires(_Tp&& __t)
|
||||
concept __adl_rend = __class_or_enum<remove_reference_t<_Tp>>
|
||||
&& requires(_Tp& __t)
|
||||
{
|
||||
{ __decay_copy(rend(std::forward<_Tp>(__t))) }
|
||||
{ __decay_copy(rend(__t)) }
|
||||
-> sentinel_for<decltype(_RBegin{}(std::forward<_Tp>(__t)))>;
|
||||
};
|
||||
|
||||
@ -600,14 +605,14 @@ namespace ranges
|
||||
_S_noexcept()
|
||||
{
|
||||
if constexpr (__member_rend<_Tp>)
|
||||
return noexcept(__decay_copy(std::declval<_Tp>().rend()));
|
||||
return noexcept(__decay_copy(std::declval<_Tp&>().rend()));
|
||||
else if constexpr (__adl_rend<_Tp>)
|
||||
return noexcept(__decay_copy(rend(std::declval<_Tp>())));
|
||||
return noexcept(__decay_copy(rend(std::declval<_Tp&>())));
|
||||
else
|
||||
{
|
||||
if constexpr (noexcept(_Begin{}(std::declval<_Tp>())))
|
||||
if constexpr (noexcept(_Begin{}(std::declval<_Tp&>())))
|
||||
{
|
||||
using _It = decltype(_Begin{}(std::declval<_Tp>()));
|
||||
using _It = decltype(_Begin{}(std::declval<_Tp&>()));
|
||||
// std::reverse_iterator copy-initializes its member.
|
||||
return is_nothrow_copy_constructible_v<_It>;
|
||||
}
|
||||
@ -617,18 +622,18 @@ namespace ranges
|
||||
}
|
||||
|
||||
public:
|
||||
template<typename _Tp>
|
||||
template<__maybe_safe_range _Tp>
|
||||
requires __member_rend<_Tp> || __adl_rend<_Tp> || __reversable<_Tp>
|
||||
constexpr auto
|
||||
operator()(_Tp&& __e) const
|
||||
operator()(_Tp&& __t) const
|
||||
noexcept(_S_noexcept<_Tp>())
|
||||
{
|
||||
if constexpr (__member_rend<_Tp>)
|
||||
return __e.rend();
|
||||
return __t.rend();
|
||||
else if constexpr (__adl_rend<_Tp>)
|
||||
return rend(std::forward<_Tp>(__e));
|
||||
return rend(__t);
|
||||
else
|
||||
return std::make_reverse_iterator(_Begin{}(std::forward<_Tp>(__e)));
|
||||
return std::make_reverse_iterator(_Begin{}(__t));
|
||||
}
|
||||
};
|
||||
|
||||
@ -667,8 +672,7 @@ namespace ranges
|
||||
template<typename _Tp> void size(_Tp&&) = delete;
|
||||
|
||||
template<typename _Tp>
|
||||
concept __adl_size
|
||||
= std::__detail::__class_or_enum<remove_reference_t<_Tp>>
|
||||
concept __adl_size = __class_or_enum<remove_reference_t<_Tp>>
|
||||
&& !disable_sized_range<remove_cvref_t<_Tp>>
|
||||
&& requires(_Tp&& __t)
|
||||
{
|
||||
@ -842,25 +846,26 @@ namespace ranges
|
||||
inline constexpr __cust_access::_CData cdata{};
|
||||
}
|
||||
|
||||
namespace __detail
|
||||
{
|
||||
template<typename _Tp>
|
||||
concept __range_impl = requires(_Tp&& __t) {
|
||||
ranges::begin(std::forward<_Tp>(__t));
|
||||
ranges::end(std::forward<_Tp>(__t));
|
||||
};
|
||||
|
||||
} // namespace __detail
|
||||
|
||||
/// [range.range] The range concept.
|
||||
template<typename _Tp>
|
||||
concept range = __detail::__range_impl<_Tp&>;
|
||||
concept range = requires(_Tp& __t)
|
||||
{
|
||||
ranges::begin(__t);
|
||||
ranges::end(__t);
|
||||
};
|
||||
|
||||
/// [range.range] The safe_range concept.
|
||||
template<typename _Tp>
|
||||
concept safe_range = range<_Tp> && __detail::__maybe_safe_range<_Tp>;
|
||||
|
||||
template<range _Range>
|
||||
using iterator_t = decltype(ranges::begin(std::declval<_Range&>()));
|
||||
|
||||
template<range _Range>
|
||||
using sentinel_t = decltype(ranges::end(std::declval<_Range&>()));
|
||||
|
||||
template<range _Range>
|
||||
using iterator_t = decltype(ranges::begin(std::declval<_Range&>()));
|
||||
using range_difference_t = iter_difference_t<iterator_t<_Range>>;
|
||||
|
||||
template<range _Range>
|
||||
using range_value_t = iter_value_t<iterator_t<_Range>>;
|
||||
@ -872,43 +877,38 @@ namespace ranges
|
||||
using range_rvalue_reference_t
|
||||
= iter_rvalue_reference_t<iterator_t<_Range>>;
|
||||
|
||||
template<range _Range>
|
||||
using range_difference_t = iter_difference_t<iterator_t<_Range>>;
|
||||
|
||||
namespace __detail
|
||||
{
|
||||
template<typename _Tp>
|
||||
concept __forwarding_range = range<_Tp> && __range_impl<_Tp>;
|
||||
} // namespace __detail
|
||||
|
||||
/// [range.sized] The sized_range concept.
|
||||
template<typename _Tp>
|
||||
concept sized_range = range<_Tp>
|
||||
&& requires(_Tp& __t) { ranges::size(__t); };
|
||||
|
||||
template<typename>
|
||||
inline constexpr bool disable_sized_range = false;
|
||||
|
||||
// [range.refinements]
|
||||
|
||||
/// A range for which ranges::begin returns an output iterator.
|
||||
template<typename _Range, typename _Tp>
|
||||
concept output_range
|
||||
= range<_Range> && output_iterator<iterator_t<_Range>, _Tp>;
|
||||
|
||||
/// A range for which ranges::begin returns an input iterator.
|
||||
template<typename _Tp>
|
||||
concept input_range = range<_Tp> && input_iterator<iterator_t<_Tp>>;
|
||||
|
||||
/// A range for which ranges::begin returns a forward iterator.
|
||||
template<typename _Tp>
|
||||
concept forward_range
|
||||
= input_range<_Tp> && forward_iterator<iterator_t<_Tp>>;
|
||||
|
||||
/// A range for which ranges::begin returns a bidirectional iterator.
|
||||
template<typename _Tp>
|
||||
concept bidirectional_range
|
||||
= forward_range<_Tp> && bidirectional_iterator<iterator_t<_Tp>>;
|
||||
|
||||
/// A range for which ranges::begin returns a random access iterator.
|
||||
template<typename _Tp>
|
||||
concept random_access_range
|
||||
= bidirectional_range<_Tp> && random_access_iterator<iterator_t<_Tp>>;
|
||||
|
||||
/// A range for which ranges::begin returns a contiguous iterator.
|
||||
template<typename _Tp>
|
||||
concept contiguous_range
|
||||
= random_access_range<_Tp> && contiguous_iterator<iterator_t<_Tp>>
|
||||
@ -917,11 +917,12 @@ namespace ranges
|
||||
{ ranges::data(__t) } -> same_as<add_pointer_t<range_reference_t<_Tp>>>;
|
||||
};
|
||||
|
||||
/// A range for which ranges::begin and ranges::end return the same type.
|
||||
template<typename _Tp>
|
||||
concept common_range
|
||||
= range<_Tp> && same_as<iterator_t<_Tp>, sentinel_t<_Tp>>;
|
||||
|
||||
// [range.iter.ops] range iterator operations
|
||||
// [range.iter.ops] range iterator operations
|
||||
|
||||
template<input_or_output_iterator _It>
|
||||
constexpr void
|
||||
|
@ -691,6 +691,18 @@ namespace experimental
|
||||
} // namespace literals
|
||||
} // namespace experimental
|
||||
|
||||
#if __cpp_lib_concepts
|
||||
namespace ranges
|
||||
{
|
||||
template<typename> extern inline const bool enable_safe_range;
|
||||
// Opt-in to safe_range concept
|
||||
template<typename _CharT, typename _Traits>
|
||||
inline constexpr bool
|
||||
enable_safe_range<experimental::basic_string_view<_CharT, _Traits>>
|
||||
= true;
|
||||
}
|
||||
#endif
|
||||
|
||||
_GLIBCXX_END_NAMESPACE_VERSION
|
||||
} // namespace std
|
||||
|
||||
|
@ -89,9 +89,10 @@ namespace ranges
|
||||
= range<_Tp> && movable<_Tp> && default_initializable<_Tp>
|
||||
&& enable_view<_Tp>;
|
||||
|
||||
/// A range which can be safely converted to a view.
|
||||
template<typename _Tp>
|
||||
concept viewable_range = range<_Tp>
|
||||
&& (__detail::__forwarding_range<_Tp> || view<decay_t<_Tp>>);
|
||||
&& (safe_range<_Tp> || view<decay_t<_Tp>>);
|
||||
|
||||
namespace __detail
|
||||
{
|
||||
@ -295,7 +296,7 @@ namespace ranges
|
||||
}
|
||||
|
||||
template<__detail::__not_same_as<subrange> _Rng>
|
||||
requires __detail::__forwarding_range<_Rng>
|
||||
requires safe_range<_Rng>
|
||||
&& convertible_to<iterator_t<_Rng>, _It>
|
||||
&& convertible_to<sentinel_t<_Rng>, _Sent>
|
||||
constexpr
|
||||
@ -306,7 +307,7 @@ namespace ranges
|
||||
_M_size._M_size = ranges::size(__r);
|
||||
}
|
||||
|
||||
template<__detail::__forwarding_range _Rng>
|
||||
template<safe_range _Rng>
|
||||
requires convertible_to<iterator_t<_Rng>, _It>
|
||||
&& convertible_to<sentinel_t<_Rng>, _Sent>
|
||||
constexpr
|
||||
@ -401,12 +402,6 @@ namespace ranges
|
||||
ranges::advance(_M_begin, __n, _M_end);
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend constexpr _It
|
||||
begin(subrange&& __r) { return __r.begin(); }
|
||||
|
||||
friend constexpr _Sent
|
||||
end(subrange&& __r) { return __r.end(); }
|
||||
};
|
||||
|
||||
template<input_or_output_iterator _It, sentinel_for<_It> _Sent>
|
||||
@ -424,14 +419,14 @@ namespace ranges
|
||||
-> subrange<tuple_element_t<0, _Pr>, tuple_element_t<1, _Pr>,
|
||||
subrange_kind::sized>;
|
||||
|
||||
template<__detail::__forwarding_range _Rng>
|
||||
template<safe_range _Rng>
|
||||
subrange(_Rng&&)
|
||||
-> subrange<iterator_t<_Rng>, sentinel_t<_Rng>,
|
||||
(sized_range<_Rng>
|
||||
|| sized_sentinel_for<sentinel_t<_Rng>, iterator_t<_Rng>>)
|
||||
? subrange_kind::sized : subrange_kind::unsized>;
|
||||
|
||||
template<__detail::__forwarding_range _Rng>
|
||||
template<safe_range _Rng>
|
||||
subrange(_Rng&&,
|
||||
__detail::__make_unsigned_like_t<range_difference_t<_Rng>>)
|
||||
-> subrange<iterator_t<_Rng>, sentinel_t<_Rng>, subrange_kind::sized>;
|
||||
@ -457,6 +452,12 @@ namespace ranges
|
||||
else
|
||||
return __r.end();
|
||||
}
|
||||
|
||||
template<input_or_output_iterator _It, sentinel_for<_It> _Sent,
|
||||
subrange_kind _Kind>
|
||||
inline constexpr bool
|
||||
enable_safe_range<subrange<_It, _Sent, _Kind>> = true;
|
||||
|
||||
} // namespace ranges
|
||||
|
||||
using ranges::get;
|
||||
@ -471,8 +472,19 @@ namespace ranges
|
||||
constexpr dangling(_Args&&...) noexcept { }
|
||||
};
|
||||
|
||||
template<range _Range>
|
||||
using safe_iterator_t = conditional_t<safe_range<_Range>,
|
||||
iterator_t<_Range>,
|
||||
dangling>;
|
||||
|
||||
template<range _Range>
|
||||
using safe_subrange_t = conditional_t<safe_range<_Range>,
|
||||
subrange<iterator_t<_Range>>,
|
||||
dangling>;
|
||||
|
||||
template<typename _Tp> requires is_object_v<_Tp>
|
||||
class empty_view : public view_interface<empty_view<_Tp>>
|
||||
class empty_view
|
||||
: public view_interface<empty_view<_Tp>>
|
||||
{
|
||||
public:
|
||||
static constexpr _Tp* begin() noexcept { return nullptr; }
|
||||
@ -480,11 +492,11 @@ namespace ranges
|
||||
static constexpr _Tp* data() noexcept { return nullptr; }
|
||||
static constexpr size_t size() noexcept { return 0; }
|
||||
static constexpr bool empty() noexcept { return true; }
|
||||
|
||||
friend constexpr _Tp* begin(empty_view) noexcept { return nullptr; }
|
||||
friend constexpr _Tp* end(empty_view) noexcept { return nullptr; }
|
||||
};
|
||||
|
||||
template<typename _Tp>
|
||||
inline constexpr bool enable_safe_range<empty_view<_Tp>> = true;
|
||||
|
||||
namespace __detail
|
||||
{
|
||||
template<copy_constructible _Tp> requires is_object_v<_Tp>
|
||||
@ -899,6 +911,9 @@ namespace ranges
|
||||
== __detail::__is_signed_integer_like<_Bound>))
|
||||
iota_view(_Winc, _Bound) -> iota_view<_Winc, _Bound>;
|
||||
|
||||
template<weakly_incrementable _Winc, semiregular _Bound>
|
||||
inline constexpr bool enable_safe_range<iota_view<_Winc, _Bound>> = true;
|
||||
|
||||
namespace views
|
||||
{
|
||||
template<typename _Tp>
|
||||
|
@ -399,16 +399,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
return {this->data() + __offset, __count};
|
||||
}
|
||||
|
||||
// observers: range helpers
|
||||
|
||||
friend constexpr iterator
|
||||
begin(span __sp) noexcept
|
||||
{ return __sp.begin(); }
|
||||
|
||||
friend constexpr iterator
|
||||
end(span __sp) noexcept
|
||||
{ return __sp.end(); }
|
||||
|
||||
private:
|
||||
[[no_unique_address]] __detail::__extent_storage<extent> _M_extent;
|
||||
pointer _M_ptr;
|
||||
@ -478,6 +468,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
using type = _Type;
|
||||
};
|
||||
|
||||
namespace ranges
|
||||
{
|
||||
template<typename> extern inline const bool enable_safe_range;
|
||||
// Opt-in to safe_range concept
|
||||
template<typename _ElementType, size_t _Extent>
|
||||
inline constexpr bool
|
||||
enable_safe_range<span<_ElementType, _Extent>> = true;
|
||||
}
|
||||
_GLIBCXX_END_NAMESPACE_VERSION
|
||||
} // namespace std
|
||||
#endif // concepts
|
||||
|
@ -724,6 +724,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
} // namespace string_literals
|
||||
} // namespace literals
|
||||
|
||||
#if __cpp_lib_concepts
|
||||
namespace ranges
|
||||
{
|
||||
template<typename> extern inline const bool enable_safe_range;
|
||||
// Opt-in to safe_range concept
|
||||
template<typename _CharT, typename _Traits>
|
||||
inline constexpr bool
|
||||
enable_safe_range<basic_string_view<_CharT, _Traits>> = true;
|
||||
}
|
||||
#endif
|
||||
_GLIBCXX_END_NAMESPACE_VERSION
|
||||
} // namespace std
|
||||
|
||||
|
@ -71,11 +71,22 @@ struct R
|
||||
int a[4] = { 0, 1, 2, 3 };
|
||||
|
||||
friend int* begin(R& r) { return r.a + 0; }
|
||||
friend int* begin(R&& r) { return r.a + 1; }
|
||||
friend int* begin(R&& r); // this overload is not defined
|
||||
friend const int* begin(const R& r) noexcept { return r.a + 2; }
|
||||
friend const int* begin(const R&& r) noexcept { return r.a + 3; }
|
||||
friend const int* begin(const R&& r) noexcept; // not defined
|
||||
};
|
||||
|
||||
struct RV // view on an R
|
||||
{
|
||||
R& r;
|
||||
|
||||
friend int* begin(RV& rv) { return begin(rv.r); }
|
||||
friend const int* begin(const RV& rv) noexcept { return begin(rv.r); }
|
||||
};
|
||||
|
||||
// Allow ranges::begin to work with RV&&
|
||||
template<> constexpr bool std::ranges::enable_safe_range<RV> = true;
|
||||
|
||||
void
|
||||
test03()
|
||||
{
|
||||
@ -86,20 +97,23 @@ test03()
|
||||
static_assert(!noexcept(std::ranges::begin(r)));
|
||||
VERIFY( std::ranges::begin(r) == begin(r) );
|
||||
|
||||
static_assert(same_as<decltype(std::ranges::begin(std::move(r))),
|
||||
decltype(begin(std::move(r)))>);
|
||||
static_assert(!noexcept(std::ranges::begin(std::move(r))));
|
||||
VERIFY( std::ranges::begin(std::move(r)) == begin(std::move(r)) );
|
||||
|
||||
|
||||
static_assert(same_as<decltype(std::ranges::begin(c)), decltype(begin(c))>);
|
||||
static_assert(noexcept(std::ranges::begin(c)));
|
||||
VERIFY( std::ranges::begin(c) == begin(c) );
|
||||
|
||||
static_assert(same_as<decltype(std::ranges::begin(std::move(c))),
|
||||
decltype(begin(std::move(c)))>);
|
||||
static_assert(noexcept(std::ranges::begin(std::move(c))));
|
||||
VERIFY( std::ranges::begin(std::move(c)) == begin(std::move(c)) );
|
||||
RV v{r};
|
||||
// enable_safe_range<RV> allows ranges::begin to work for rvalues,
|
||||
// but it will call v.begin() or begin(v) on an lvalue:
|
||||
static_assert(same_as<decltype(std::ranges::begin(std::move(v))),
|
||||
decltype(begin(v))>);
|
||||
static_assert(!noexcept(std::ranges::begin(std::move(v))));
|
||||
VERIFY( std::ranges::begin(std::move(v)) == begin(v) );
|
||||
|
||||
const RV cv{r};
|
||||
static_assert(same_as<decltype(std::ranges::begin(std::move(cv))),
|
||||
decltype(begin(cv))>);
|
||||
static_assert(noexcept(std::ranges::begin(std::move(cv))));
|
||||
VERIFY( std::ranges::begin(std::move(cv)) == begin(cv) );
|
||||
}
|
||||
|
||||
struct RR
|
||||
@ -111,12 +125,15 @@ struct RR
|
||||
short* begin() noexcept { return &s; }
|
||||
const long* begin() const { return &l; }
|
||||
|
||||
friend int* begin(RR& r) { return r.a + 0; }
|
||||
friend int* begin(RR&& r) { return r.a + 1; }
|
||||
friend int* begin(RR& r) noexcept { return r.a + 0; }
|
||||
friend int* begin(RR&& r); // not defined
|
||||
friend const int* begin(const RR& r) { return r.a + 2; }
|
||||
friend const int* begin(const RR&& r) noexcept { return r.a + 3; }
|
||||
friend const int* begin(const RR&& r) noexcept; // not defined
|
||||
};
|
||||
|
||||
// N.B. this is a lie, begin on an RR rvalue will return a dangling pointer.
|
||||
template<> constexpr bool std::ranges::enable_safe_range<RR> = true;
|
||||
|
||||
void
|
||||
test04()
|
||||
{
|
||||
@ -125,14 +142,16 @@ test04()
|
||||
VERIFY( std::ranges::begin(r) == &r.s );
|
||||
static_assert(noexcept(std::ranges::begin(r)));
|
||||
|
||||
VERIFY( std::ranges::begin(std::move(r)) == r.a + 1 );
|
||||
static_assert(!noexcept(std::ranges::begin(std::move(r))));
|
||||
// calls r.begin() on an lvalue, not rvalue
|
||||
VERIFY( std::ranges::begin(std::move(r)) == std::ranges::begin(r) );
|
||||
static_assert(noexcept(std::ranges::begin(std::move(r))));
|
||||
|
||||
VERIFY( std::ranges::begin(c) == &r.l );
|
||||
static_assert(!noexcept(std::ranges::begin(c)));
|
||||
|
||||
VERIFY( std::ranges::begin(std::move(c)) == r.a + 3 );
|
||||
static_assert(noexcept(std::ranges::begin(std::move(c))));
|
||||
// calls r.begin() on a const lvalue, not rvalue
|
||||
VERIFY( std::ranges::begin(std::move(c)) == std::ranges::begin(c) );
|
||||
static_assert(!noexcept(std::ranges::begin(std::move(c))));
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -40,20 +40,34 @@ struct R
|
||||
int a[4] = { 0, 1, 2, 3 };
|
||||
|
||||
friend int* begin(R& r) { return r.a + 0; }
|
||||
friend int* begin(R&& r) { return r.a + 1; }
|
||||
friend int* begin(R&&); // this function is not defined
|
||||
friend const int* begin(const R& r) noexcept { return r.a + 2; }
|
||||
friend const int* begin(const R&& r) noexcept { return r.a + 3; }
|
||||
friend const int* begin(const R&&); // this function is not defined
|
||||
};
|
||||
|
||||
struct RV // view on an R
|
||||
{
|
||||
R& r;
|
||||
|
||||
friend int* begin(RV&); // this function is not defined
|
||||
friend const int* begin(const RV& rv) noexcept { return begin(std::as_const(rv.r)); }
|
||||
};
|
||||
|
||||
// Allow ranges::begin to work with RV&&
|
||||
template<> constexpr bool std::ranges::enable_safe_range<RV> = true;
|
||||
|
||||
void
|
||||
test03()
|
||||
{
|
||||
R r;
|
||||
const R& c = r;
|
||||
VERIFY(std::ranges::cbegin(r) == std::ranges::begin(c));
|
||||
VERIFY(std::ranges::cbegin(std::move(r)) == std::ranges::begin(std::move(c)));
|
||||
VERIFY(std::ranges::cbegin(c) == std::ranges::begin(c));
|
||||
VERIFY(std::ranges::cbegin(std::move(c)) == std::ranges::begin(std::move(c)));
|
||||
|
||||
RV v{r};
|
||||
VERIFY(std::ranges::cbegin(std::move(v)) == std::ranges::begin(c));
|
||||
const RV cv{r};
|
||||
VERIFY(std::ranges::cbegin(std::move(cv)) == std::ranges::begin(c));
|
||||
}
|
||||
|
||||
struct RR
|
||||
@ -71,15 +85,18 @@ struct RR
|
||||
friend const int* begin(const RR&& r) noexcept { return r.a + 3; }
|
||||
};
|
||||
|
||||
// N.B. this is a lie, cbegin on an RR rvalue will return a dangling pointer.
|
||||
template<> constexpr bool std::ranges::enable_safe_range<RR> = true;
|
||||
|
||||
void
|
||||
test04()
|
||||
{
|
||||
RR r;
|
||||
const RR& c = r;
|
||||
VERIFY(std::ranges::cbegin(r) == std::ranges::begin(c));
|
||||
VERIFY(std::ranges::cbegin(std::move(r)) == std::ranges::begin(std::move(c)));
|
||||
VERIFY(std::ranges::cbegin(std::move(r)) == std::ranges::begin(c));
|
||||
VERIFY(std::ranges::cbegin(c) == std::ranges::begin(c));
|
||||
VERIFY(std::ranges::cbegin(std::move(c)) == std::ranges::begin(std::move(c)));
|
||||
VERIFY(std::ranges::cbegin(std::move(c)) == std::ranges::begin(c));
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -51,17 +51,22 @@ struct R
|
||||
long l = 0;
|
||||
|
||||
int* data() const { return nullptr; }
|
||||
friend long* begin(R&& r) { return &r.l; }
|
||||
friend const long* begin(const R&& r) { return &r.l + 1; }
|
||||
friend long* begin(R&& r); // this function is not defined
|
||||
friend const long* begin(const R& r) { return &r.l; }
|
||||
friend const short* begin(const R&&); // not defined
|
||||
};
|
||||
|
||||
// This is a lie, ranges::begin(R&&) returns a dangling iterator.
|
||||
template<> constexpr bool std::ranges::enable_safe_range<R> = true;
|
||||
|
||||
void
|
||||
test03()
|
||||
{
|
||||
R r;
|
||||
const R& c = r;
|
||||
VERIFY( std::ranges::cdata(std::move(r)) == std::ranges::data(std::move(c)) );
|
||||
VERIFY( std::ranges::cdata(std::move(c)) == std::ranges::data(std::move(c)) );
|
||||
VERIFY( std::ranges::cdata(r) == std::ranges::data(c) );
|
||||
VERIFY( std::ranges::cdata(std::move(r)) == std::ranges::begin(c) );
|
||||
VERIFY( std::ranges::cdata(std::move(c)) == std::ranges::begin(c) );
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -49,15 +49,31 @@ struct R
|
||||
friend const int* end(const R&& r) noexcept { return r.a + 3; }
|
||||
};
|
||||
|
||||
struct RV // view on an R
|
||||
{
|
||||
R& r;
|
||||
|
||||
friend const int* begin(RV& rv) { return rv.r.begin(); }
|
||||
friend int* end(RV& rv) { return end(rv.r); }
|
||||
friend const int* begin(const RV& rv) noexcept { return rv.r.begin(); }
|
||||
friend const int* end(const RV& rv) noexcept { return end(std::as_const(rv.r)); }
|
||||
};
|
||||
|
||||
// Allow ranges::end to work with RV&&
|
||||
template<> constexpr bool std::ranges::enable_safe_range<RV> = true;
|
||||
|
||||
void
|
||||
test03()
|
||||
{
|
||||
R r;
|
||||
const R& c = r;
|
||||
VERIFY( std::ranges::cend(r) == std::ranges::end(c) );
|
||||
VERIFY( std::ranges::cend(std::move(r)) == std::ranges::end(std::move(c)) );
|
||||
VERIFY( std::ranges::cend(c) == std::ranges::end(c) );
|
||||
VERIFY( std::ranges::cend(std::move(c)) == std::ranges::end(std::move(c)) );
|
||||
|
||||
RV v{r};
|
||||
const RV cv{r};
|
||||
VERIFY( std::ranges::cend(std::move(v)) == std::ranges::end(c) );
|
||||
VERIFY( std::ranges::cend(std::move(cv)) == std::ranges::end(c) );
|
||||
}
|
||||
|
||||
struct RR
|
||||
@ -81,15 +97,19 @@ struct RR
|
||||
friend const int* end(const RR&& r) noexcept { return r.a + 3; }
|
||||
};
|
||||
|
||||
// N.B. this is a lie, begin/end on an RR rvalue will return a dangling pointer.
|
||||
template<> constexpr bool std::ranges::enable_safe_range<RR> = true;
|
||||
|
||||
void
|
||||
test04()
|
||||
{
|
||||
RR r;
|
||||
const RR& c = r;
|
||||
VERIFY( std::ranges::cend(r) == std::ranges::end(c) );
|
||||
VERIFY( std::ranges::cend(std::move(r)) == std::ranges::end(std::move(c)) );
|
||||
VERIFY( std::ranges::cend(c) == std::ranges::end(c) );
|
||||
VERIFY( std::ranges::cend(std::move(c)) == std::ranges::end(std::move(c)) );
|
||||
|
||||
VERIFY( std::ranges::cend(std::move(r)) == std::ranges::end(c) );
|
||||
VERIFY( std::ranges::cend(std::move(c)) == std::ranges::end(c) );
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -31,15 +31,29 @@ struct R1
|
||||
friend const int* rbegin(const R1&& r) { return &r.j; }
|
||||
};
|
||||
|
||||
struct R1V // view on an R1
|
||||
{
|
||||
R1& r;
|
||||
|
||||
friend const long* rbegin(R1V&); // this is not defined
|
||||
friend const int* rbegin(const R1V& rv) noexcept { return rv.r.rbegin(); }
|
||||
};
|
||||
|
||||
// Allow ranges::end to work with R1V&&
|
||||
template<> constexpr bool std::ranges::enable_safe_range<R1V> = true;
|
||||
|
||||
void
|
||||
test01()
|
||||
{
|
||||
R1 r;
|
||||
const R1& c = r;
|
||||
VERIFY( std::ranges::crbegin(r) == std::ranges::rbegin(c) );
|
||||
VERIFY( std::ranges::crbegin(std::move(r)) == std::ranges::rbegin(std::move(c)) );
|
||||
VERIFY( std::ranges::crbegin(c) == std::ranges::rbegin(c) );
|
||||
VERIFY( std::ranges::crbegin(std::move(c)) == std::ranges::rbegin(std::move(c)) );
|
||||
|
||||
R1V v{r};
|
||||
const R1V cv{r};
|
||||
VERIFY( std::ranges::crbegin(std::move(v)) == std::ranges::rbegin(c) );
|
||||
VERIFY( std::ranges::crbegin(std::move(cv)) == std::ranges::rbegin(c) );
|
||||
}
|
||||
|
||||
struct R2
|
||||
@ -50,19 +64,23 @@ struct R2
|
||||
const int* begin() const { return a; }
|
||||
const int* end() const { return a + 2; }
|
||||
|
||||
friend const long* begin(const R2&& r) { return r.l; }
|
||||
friend const long* end(const R2&& r) { return r.l + 2; }
|
||||
friend const long* begin(const R2&&); // not defined
|
||||
friend const long* end(const R2&&); // not defined
|
||||
};
|
||||
|
||||
// N.B. this is a lie, rbegin on an R2 rvalue will return a dangling pointer.
|
||||
template<> constexpr bool std::ranges::enable_safe_range<R2> = true;
|
||||
|
||||
void
|
||||
test02()
|
||||
{
|
||||
R2 r;
|
||||
const R2& c = r;
|
||||
VERIFY( std::ranges::crbegin(r) == std::ranges::rbegin(c) );
|
||||
VERIFY( std::ranges::crbegin(std::move(r)) == std::ranges::rbegin(std::move(c)) );
|
||||
VERIFY( std::ranges::crbegin(c) == std::ranges::rbegin(c) );
|
||||
VERIFY( std::ranges::crbegin(std::move(c)) == std::ranges::rbegin(std::move(c)) );
|
||||
|
||||
VERIFY( std::ranges::crbegin(std::move(r)) == std::ranges::rbegin(c) );
|
||||
VERIFY( std::ranges::crbegin(std::move(c)) == std::ranges::rbegin(c) );
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -33,15 +33,18 @@ struct R1
|
||||
friend constexpr const int* rend(const R1&& r) { return &r.j + 1; }
|
||||
};
|
||||
|
||||
// N.B. this is a lie, rend on an R1 rvalue will return a dangling pointer.
|
||||
template<> constexpr bool std::ranges::enable_safe_range<R1> = true;
|
||||
|
||||
void
|
||||
test01()
|
||||
{
|
||||
R1 r;
|
||||
const R1& c = r;
|
||||
VERIFY( std::ranges::crend(r) == std::ranges::rend(c) );
|
||||
VERIFY( std::ranges::crend(std::move(r)) == std::ranges::rend(std::move(c)) );
|
||||
VERIFY( std::ranges::crend(c) == std::ranges::rend(c) );
|
||||
VERIFY( std::ranges::crend(std::move(c)) == std::ranges::rend(std::move(c)) );
|
||||
VERIFY( std::ranges::crend(std::move(r)) == std::ranges::rend(c) );
|
||||
VERIFY( std::ranges::crend(std::move(c)) == std::ranges::rend(c) );
|
||||
}
|
||||
|
||||
struct R2
|
||||
@ -56,14 +59,17 @@ struct R2
|
||||
friend const long* end(const R2&& r) { return r.l + 2; }
|
||||
};
|
||||
|
||||
// N.B. this is a lie, rend on an R2 rvalue will return a dangling pointer.
|
||||
template<> constexpr bool std::ranges::enable_safe_range<R2> = true;
|
||||
|
||||
void
|
||||
test02()
|
||||
{
|
||||
R2 r;
|
||||
const R2& c = r;
|
||||
VERIFY( std::ranges::crend(r) == std::ranges::rend(c) );
|
||||
VERIFY( std::ranges::crend(std::move(r)) == std::ranges::rend(std::move(c)) );
|
||||
VERIFY( std::ranges::crend(c) == std::ranges::rend(c) );
|
||||
VERIFY( std::ranges::crend(std::move(r)) == std::ranges::rend(std::move(c)) );
|
||||
VERIFY( std::ranges::crend(std::move(c)) == std::ranges::rend(std::move(c)) );
|
||||
}
|
||||
|
||||
@ -78,6 +84,9 @@ struct R3
|
||||
friend const int* rend(const R3& r) { return &r.i; }
|
||||
};
|
||||
|
||||
// N.B. this is a lie, rend on an R3 rvalue will return a dangling pointer.
|
||||
template<> constexpr bool std::ranges::enable_safe_range<R3> = true;
|
||||
|
||||
void
|
||||
test03()
|
||||
{
|
||||
|
@ -56,15 +56,20 @@ struct R3
|
||||
long l = 0;
|
||||
|
||||
int* data() const { return nullptr; }
|
||||
friend long* begin(R3&& r) { return &r.l; }
|
||||
friend const long* begin(const R3&& r) { return &r.l + 1; }
|
||||
friend long* begin(R3& r) { return &r.l; }
|
||||
friend const long* begin(const R3& r) { return &r.l + 1; }
|
||||
};
|
||||
|
||||
// N.B. this is a lie, begin on an R3 rvalue will return a dangling pointer.
|
||||
template<> constexpr bool std::ranges::enable_safe_range<R3> = true;
|
||||
|
||||
void
|
||||
test03()
|
||||
{
|
||||
R3 r;
|
||||
const R3& c = r;
|
||||
// r.data() can only be used on an lvalue, but ranges::begin(R3&&) is OK
|
||||
// because R3 satisfies ranges::safe_range.
|
||||
VERIFY( std::ranges::data(std::move(r)) == std::to_address(std::ranges::begin(std::move(r))) );
|
||||
VERIFY( std::ranges::data(std::move(c)) == std::to_address(std::ranges::begin(std::move(c))) );
|
||||
}
|
||||
|
@ -74,6 +74,19 @@ struct R
|
||||
friend const int* end(const R&& r) noexcept { return r.a + 3; }
|
||||
};
|
||||
|
||||
struct RV // view on an R
|
||||
{
|
||||
R& r;
|
||||
|
||||
const int* begin() const;
|
||||
|
||||
friend int* end(RV& v) noexcept { return end(v.r); }
|
||||
friend const int* end(const RV& v) { return end(std::as_const(v.r)); }
|
||||
};
|
||||
|
||||
// Allow ranges::begin to work with RV&&
|
||||
template<> constexpr bool std::ranges::enable_safe_range<RV> = true;
|
||||
|
||||
void
|
||||
test03()
|
||||
{
|
||||
@ -84,20 +97,21 @@ test03()
|
||||
static_assert(!noexcept(std::ranges::end(r)));
|
||||
VERIFY( std::ranges::end(r) == end(r) );
|
||||
|
||||
static_assert(same_as<decltype(std::ranges::end(std::move(r))),
|
||||
decltype(end(std::move(r)))>);
|
||||
static_assert(!noexcept(std::ranges::end(std::move(r))));
|
||||
VERIFY( std::ranges::end(std::move(r)) == end(std::move(r)) );
|
||||
|
||||
|
||||
static_assert(same_as<decltype(std::ranges::end(c)), decltype(end(c))>);
|
||||
static_assert(noexcept(std::ranges::end(c)));
|
||||
VERIFY( std::ranges::end(c) == end(c) );
|
||||
|
||||
static_assert(same_as<decltype(std::ranges::end(std::move(c))),
|
||||
decltype(end(std::move(c)))>);
|
||||
static_assert(noexcept(std::ranges::end(std::move(c))));
|
||||
VERIFY( std::ranges::end(std::move(c)) == end(std::move(c)) );
|
||||
RV v{r};
|
||||
static_assert(same_as<decltype(std::ranges::end(std::move(v))),
|
||||
decltype(end(r))>);
|
||||
static_assert(noexcept(std::ranges::end(std::move(v))));
|
||||
VERIFY( std::ranges::end(std::move(v)) == end(r) );
|
||||
|
||||
const RV cv{r};
|
||||
static_assert(same_as<decltype(std::ranges::end(std::move(cv))),
|
||||
decltype(end(c))>);
|
||||
static_assert(!noexcept(std::ranges::end(std::move(cv))));
|
||||
VERIFY( std::ranges::end(std::move(cv)) == end(c) );
|
||||
}
|
||||
|
||||
struct RR
|
||||
@ -123,6 +137,9 @@ struct RR
|
||||
friend const int* end(const RR&& r) noexcept { return r.a + 3; }
|
||||
};
|
||||
|
||||
// N.B. this is a lie, end on an RR rvalue will return a dangling pointer.
|
||||
template<> constexpr bool std::ranges::enable_safe_range<RR> = true;
|
||||
|
||||
void
|
||||
test04()
|
||||
{
|
||||
@ -131,14 +148,14 @@ test04()
|
||||
VERIFY( std::ranges::end(r) == &r.s );
|
||||
static_assert(noexcept(std::ranges::end(r)));
|
||||
|
||||
VERIFY( std::ranges::end(std::move(r)) == r.a + 1 );
|
||||
static_assert(!noexcept(std::ranges::end(std::move(r))));
|
||||
VERIFY( std::ranges::end(std::move(r)) == &r.s );
|
||||
static_assert(noexcept(std::ranges::end(std::move(r))));
|
||||
|
||||
VERIFY( std::ranges::end(c) == &r.l );
|
||||
static_assert(!noexcept(std::ranges::end(c)));
|
||||
|
||||
VERIFY( std::ranges::end(std::move(c)) == r.a + 3 );
|
||||
static_assert(noexcept(std::ranges::end(std::move(c))));
|
||||
VERIFY( std::ranges::end(std::move(c)) == &r.l );
|
||||
static_assert(!noexcept(std::ranges::end(std::move(c))));
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -31,26 +31,31 @@ struct R1
|
||||
friend constexpr const int* rbegin(const R1&& r) { return &r.j; }
|
||||
};
|
||||
|
||||
// N.B. this is a lie, rbegin on an R1 rvalue will return a dangling pointer.
|
||||
template<> constexpr bool std::ranges::enable_safe_range<R1> = true;
|
||||
|
||||
void
|
||||
test01()
|
||||
{
|
||||
constexpr R1 r;
|
||||
static_assert( std::ranges::rbegin(r) == &r.i );
|
||||
static_assert( std::ranges::rbegin(std::move(r)) == &r.j );
|
||||
static_assert( std::ranges::rbegin(std::move(r)) == &r.i );
|
||||
}
|
||||
|
||||
struct R2
|
||||
{
|
||||
int a[2] = { };
|
||||
long l[2] = { };
|
||||
|
||||
constexpr const int* begin() const { return a; }
|
||||
constexpr const int* end() const { return a + 2; }
|
||||
|
||||
friend constexpr const long* begin(const R2&& r) { return r.l; }
|
||||
friend constexpr const long* end(const R2&& r) { return r.l + 2; }
|
||||
friend constexpr const long* begin(const R2&&); // not defined
|
||||
friend constexpr const long* end(const R2&&); // not defined
|
||||
};
|
||||
|
||||
// N.B. this is a lie, begin/end on an R2 rvalue will return a dangling pointer.
|
||||
template<> constexpr bool std::ranges::enable_safe_range<R2> = true;
|
||||
|
||||
void
|
||||
test02()
|
||||
{
|
||||
|
@ -29,16 +29,19 @@ struct R1
|
||||
|
||||
constexpr const int* rbegin() const { return &i; }
|
||||
constexpr const int* rend() const { return &i + 1; }
|
||||
friend constexpr const int* rbegin(const R1&& r) { return &r.j; }
|
||||
friend constexpr const int* rend(const R1&& r) { return &r.j + 1; }
|
||||
friend constexpr const int* rbegin(const R1&&); // not defined
|
||||
friend constexpr const int* rend(const R1&&); // not defined
|
||||
};
|
||||
|
||||
// N.B. this is a lie, rend on an R1 rvalue will return a dangling pointer.
|
||||
template<> constexpr bool std::ranges::enable_safe_range<R1> = true;
|
||||
|
||||
void
|
||||
test01()
|
||||
{
|
||||
constexpr R1 r;
|
||||
static_assert( std::ranges::rend(r) == &r.i + 1 );
|
||||
static_assert( std::ranges::rend(std::move(r)) == &r.j + 1 );
|
||||
static_assert( std::ranges::rend(std::move(r)) == &r.i + 1 );
|
||||
}
|
||||
|
||||
struct R2
|
||||
@ -53,6 +56,9 @@ struct R2
|
||||
friend constexpr const long* end(const R2&& r) { return r.l + 2; }
|
||||
};
|
||||
|
||||
// N.B. this is a lie, begin/end on an R2 rvalue will return a dangling pointer.
|
||||
template<> constexpr bool std::ranges::enable_safe_range<R2> = true;
|
||||
|
||||
void
|
||||
test02()
|
||||
{
|
||||
|
@ -31,5 +31,5 @@ static_assert(e.end() == nullptr);
|
||||
static_assert(e.data() == nullptr);
|
||||
static_assert(e.empty());
|
||||
|
||||
static_assert(begin(e) == nullptr);
|
||||
static_assert(end(e) == nullptr);
|
||||
static_assert(std::ranges::begin(e) == nullptr);
|
||||
static_assert(std::ranges::end(e) == nullptr);
|
||||
|
41
libstdc++-v3/testsuite/std/ranges/safe_range.cc
Normal file
41
libstdc++-v3/testsuite/std/ranges/safe_range.cc
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright (C) 2019 Free Software Foundation, Inc.
|
||||
//
|
||||
// This file is part of the GNU ISO C++ Library. This library is free
|
||||
// software; you can redistribute it and/or modify it under the
|
||||
// terms of the GNU General Public License as published by the
|
||||
// Free Software Foundation; either version 3, or (at your option)
|
||||
// any later version.
|
||||
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this library; see the file COPYING3. If not see
|
||||
// <http://www.gnu.org/licenses/>.
|
||||
|
||||
// { dg-options "-std=gnu++2a" }
|
||||
// { dg-do compile { target c++2a } }
|
||||
|
||||
#include <ranges>
|
||||
#include <testsuite_iterators.h>
|
||||
|
||||
static_assert( std::ranges::safe_range<int(&)[1]> );
|
||||
static_assert( std::ranges::safe_range<const int(&)[1]> );
|
||||
static_assert( !std::ranges::safe_range<int[1]> );
|
||||
static_assert( !std::ranges::safe_range<int*> );
|
||||
|
||||
using __gnu_test::test_contiguous_range;
|
||||
|
||||
static_assert( !std::ranges::safe_range<test_contiguous_range<int>> );
|
||||
static_assert( std::ranges::safe_range<test_contiguous_range<int>&> );
|
||||
static_assert( !std::ranges::safe_range<test_contiguous_range<int>&&> );
|
||||
|
||||
template<>
|
||||
constexpr bool
|
||||
std::ranges::enable_safe_range<test_contiguous_range<long>> = true;
|
||||
|
||||
static_assert( std::ranges::safe_range<test_contiguous_range<long>> );
|
||||
static_assert( std::ranges::safe_range<test_contiguous_range<long>&> );
|
||||
static_assert( std::ranges::safe_range<test_contiguous_range<long>&&> );
|
59
libstdc++-v3/testsuite/std/ranges/safe_range_types.cc
Normal file
59
libstdc++-v3/testsuite/std/ranges/safe_range_types.cc
Normal file
@ -0,0 +1,59 @@
|
||||
// Copyright (C) 2019 Free Software Foundation, Inc.
|
||||
//
|
||||
// This file is part of the GNU ISO C++ Library. This library is free
|
||||
// software; you can redistribute it and/or modify it under the
|
||||
// terms of the GNU General Public License as published by the
|
||||
// Free Software Foundation; either version 3, or (at your option)
|
||||
// any later version.
|
||||
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this library; see the file COPYING3. If not see
|
||||
// <http://www.gnu.org/licenses/>.
|
||||
|
||||
// { dg-options "-std=gnu++2a" }
|
||||
// { dg-do compile { target c++2a } }
|
||||
|
||||
#include <ranges>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <experimental/string_view>
|
||||
|
||||
template<typename T>
|
||||
constexpr bool
|
||||
rvalue_is_safe_range()
|
||||
{
|
||||
using std::ranges::safe_range;
|
||||
|
||||
// An lvalue range always models safe_range
|
||||
static_assert( safe_range<T&> );
|
||||
static_assert( safe_range<const T&> );
|
||||
|
||||
// Result should not depend on addition of const or rvalue-reference.
|
||||
static_assert( safe_range<T&&> == safe_range<T> );
|
||||
static_assert( safe_range<const T> == safe_range<T> );
|
||||
static_assert( safe_range<const T&&> == safe_range<T> );
|
||||
|
||||
return std::ranges::safe_range<T>;
|
||||
}
|
||||
|
||||
static_assert( rvalue_is_safe_range<std::ranges::subrange<int*, int*>>() );
|
||||
static_assert( rvalue_is_safe_range<std::ranges::empty_view<int>>() );
|
||||
static_assert( rvalue_is_safe_range<std::ranges::iota_view<int>>() );
|
||||
static_assert( rvalue_is_safe_range<std::ranges::iota_view<int, int>>() );
|
||||
|
||||
static_assert( rvalue_is_safe_range<std::span<int>>() );
|
||||
static_assert( rvalue_is_safe_range<std::span<int, 99>>() );
|
||||
|
||||
static_assert( ! rvalue_is_safe_range<std::string>() );
|
||||
static_assert( ! rvalue_is_safe_range<std::wstring>() );
|
||||
|
||||
static_assert( rvalue_is_safe_range<std::string_view>() );
|
||||
static_assert( rvalue_is_safe_range<std::wstring_view>() );
|
||||
|
||||
static_assert( rvalue_is_safe_range<std::experimental::string_view>() );
|
||||
static_assert( rvalue_is_safe_range<std::experimental::wstring_view>() );
|
@ -758,6 +758,11 @@ namespace __gnu_test
|
||||
template<typename T>
|
||||
using test_output_sized_range
|
||||
= test_sized_range<T, output_iterator_wrapper>;
|
||||
|
||||
// test_container, test_range and test_sized_range do not own their elements,
|
||||
// so they all model std::ranges::safe_range. This file does not define
|
||||
// specializations of std::ranges::enable_safe_range, so that individual
|
||||
// test can decide whether or not to do so.
|
||||
#endif // C++20
|
||||
} // namespace __gnu_test
|
||||
#endif // _TESTSUITE_ITERATORS
|
||||
|
Loading…
x
Reference in New Issue
Block a user