diff options
author | Jonathan Wakely <jwakely@redhat.com> | 2019-12-09 17:35:24 +0000 |
---|---|---|
committer | Jonathan Wakely <redi@gcc.gnu.org> | 2019-12-09 17:35:24 +0000 |
commit | b5b2e3879db9a251a2e7ff2553e188682d202c7e (patch) | |
tree | 50bc117800c5e6bc739168e9953df1fcb21505db /libstdc++-v3/include | |
parent | 43c2de7a5720e0d6fbc5df8fc6fb03f2dbf164ff (diff) | |
download | gcc-b5b2e3879db9a251a2e7ff2553e188682d202c7e.zip gcc-b5b2e3879db9a251a2e7ff2553e188682d202c7e.tar.gz gcc-b5b2e3879db9a251a2e7ff2553e188682d202c7e.tar.bz2 |
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
Diffstat (limited to 'libstdc++-v3/include')
-rw-r--r-- | libstdc++-v3/include/bits/range_access.h | 183 | ||||
-rw-r--r-- | libstdc++-v3/include/experimental/string_view | 12 | ||||
-rw-r--r-- | libstdc++-v3/include/std/ranges | 45 | ||||
-rw-r--r-- | libstdc++-v3/include/std/span | 18 | ||||
-rw-r--r-- | libstdc++-v3/include/std/string_view | 10 |
5 files changed, 152 insertions, 116 deletions
diff --git a/libstdc++-v3/include/bits/range_access.h b/libstdc++-v3/include/bits/range_access.h index c94e965..6ebd667 100644 --- a/libstdc++-v3/include/bits/range_access.h +++ b/libstdc++-v3/include/bits/range_access.h @@ -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)); + /// [range.range] The range concept. + template<typename _Tp> + concept range = requires(_Tp& __t) + { + ranges::begin(__t); + ranges::end(__t); }; - } // namespace __detail - - /// [range.range] The range concept. + /// [range.range] The safe_range concept. template<typename _Tp> - concept range = __detail::__range_impl<_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 diff --git a/libstdc++-v3/include/experimental/string_view b/libstdc++-v3/include/experimental/string_view index 84b2a3e..cc9cb93 100644 --- a/libstdc++-v3/include/experimental/string_view +++ b/libstdc++-v3/include/experimental/string_view @@ -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 diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges index e1bf6ee..c256781 100644 --- a/libstdc++-v3/include/std/ranges +++ b/libstdc++-v3/include/std/ranges @@ -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> diff --git a/libstdc++-v3/include/std/span b/libstdc++-v3/include/std/span index c71f8bc..f215dec 100644 --- a/libstdc++-v3/include/std/span +++ b/libstdc++-v3/include/std/span @@ -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 diff --git a/libstdc++-v3/include/std/string_view b/libstdc++-v3/include/std/string_view index 9d2a8e8..add432b 100644 --- a/libstdc++-v3/include/std/string_view +++ b/libstdc++-v3/include/std/string_view @@ -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 |