From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id 6EE493858414 for ; Fri, 14 Apr 2023 10:14:24 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 6EE493858414 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1681467264; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=0bf7lyambrrC6mBhQ7SKo6kM7m3GTOoz8Ke5kKnYyDA=; b=NeuhSlVUIZjkY5o9u4jJRmp9rRKPHwbb5F/Kwbw6qD0m2QJFbi3Z/Pd5P9Er481sK5CuSW H4d8dJumC2fggEJrMNUUXhBJ6+sXfaOctVXCg3waiNVKJLhBI1yoRS4xMm4aLtM7XCtScb hmHMhGXpa9N5eOzM6karBaBVstbq2vI= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-358-Qj8A1aGtOTq5PgLuRL6cuA-1; Fri, 14 Apr 2023 06:14:21 -0400 X-MC-Unique: Qj8A1aGtOTq5PgLuRL6cuA-1 Received: from smtp.corp.redhat.com (int-mx10.intmail.prod.int.rdu2.redhat.com [10.11.54.10]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id CAD358996E2; Fri, 14 Apr 2023 10:14:20 +0000 (UTC) Received: from localhost (unknown [10.33.36.209]) by smtp.corp.redhat.com (Postfix) with ESMTP id 591DA492B00; Fri, 14 Apr 2023 10:14:20 +0000 (UTC) Date: Fri, 14 Apr 2023 11:14:19 +0100 From: Jonathan Wakely To: Patrick Palka Cc: gcc-patches@gcc.gnu.org, libstdc++@gcc.gnu.org Subject: Re: [PATCH 2/2] libstdc++: Implement P2278R4 "cbegin should always return a constant iterator" Message-ID: References: <20230414040042.1498825-1-ppalka@redhat.com> <20230414040042.1498825-2-ppalka@redhat.com> MIME-Version: 1.0 In-Reply-To: <20230414040042.1498825-2-ppalka@redhat.com> X-Clacks-Overhead: GNU Terry Pratchett X-Scanned-By: MIMEDefang 3.1 on 10.11.54.10 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset=us-ascii; format=flowed Content-Disposition: inline X-Spam-Status: No, score=-11.8 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,GIT_PATCH_0,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_NONE,TXREP,T_SCC_BODY_TEXT_LINE autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: On 14/04/23 00:00 -0400, Patrick Palka wrote: >This also implements the approved follow-up LWG issues 3765, 3766, 3769, >3770, 3811, 3850, 3853, 3862 and 3872. > >Tested on x86_64-pc-linux-gnu, does this look OK for trunk? > >libstdc++-v3/ChangeLog: > > * include/bits/ranges_base.h (const_iterator_t): Define for C++23. > (const_sentinel_t): Likewise. > (range_const_reference_t): Likewise. > (constant_range): Likewise. > (__cust_access::__possibly_const_range): Likewise, replacing ... > (__cust_access::__as_const): ... this. > (__cust_access::_CBegin::operator()): Redefine for C++23 as per P2278R4. > (__cust_access::_CEnd::operator()): Likewise. > (__cust_access::_CRBegin::operator()): Likewise. > (__cust_access::_CREnd::operator()): Likewise. > (__cust_access::_CData::operator()): Likewise. > (__cust_access::_CData::__as_const_pointer): Define for C++23 > * include/bits/ranges_util.h (ranges::__detail::__different_from): > Make it an alias of std::__detail::__different_from. > (view_interface::cbegin): Define for C++23. > (view_interface::cend): Likewise. > * include/bits/stl_iterator.h (__detail::__different_from): Define. > (iter_const_reference_t): Define for C++23. > (__detail::__constant_iterator): Likewise. > (__detail::__is_const_iterator): Likewise. > (__detail::__not_a_const_iterator: Likewise. > (__detail::__iter_const_rvalue_reference_t): Likewise. > (__detail::__basic_const_iter_cat):: Likewise. > (const_iterator): Likewise. > (__detail::__const_sentinel): Likewise. > (const_sentinel): Likewise. > (basic_const_iterator): Likewise. > (common_type, _Up>): Likewise. > (common_type<_Up, basic_const_iterator<_Tp>>): Likewise. > (common_type, basic_const_iterator>): > Likewise. > (make_const_iterator): Define for C++23. > (make_const_sentinel): Likewise. > * include/std/ranges (__cpp_lib_ranges_as_const): Likewise. > (as_const_view): Likewise. > (enable_borrowed_range): Likewise. > (views::__detail::__is_ref_view): Likewise. > (views::__detail::__can_is_const_view): Likewise. > (views::_AsConst, views::as_const): Likewise. > * include/std/span (span::const_iterator): Likewise. > (span::const_reverse_iterator): Likewise. > (span::cbegin): Likewise. > (span::cend): Likewise. > (span::crbegin): Likewise. > (span::crend): Likewise. > * include/std/version (__cpp_lib_ranges_as_const): Likewise. > * testsuite/std/ranges/adaptors/join.cc (test06): Adjust to > behave independently of C++20 vs C++23. > * testsuite/std/ranges/version_c++23.cc: Verify value of > __cpp_lib_ranges_as_const macro. > * testsuite/24_iterators/const_iterator/1.cc: New test. > * testsuite/std/ranges/adaptors/as_const/1.cc: New test. >--- > libstdc++-v3/include/bits/ranges_base.h | 99 +++++ > libstdc++-v3/include/bits/ranges_util.h | 22 +- > libstdc++-v3/include/bits/stl_iterator.h | 366 ++++++++++++++++++ > libstdc++-v3/include/std/ranges | 106 +++++ > libstdc++-v3/include/std/span | 22 ++ > libstdc++-v3/include/std/version | 1 + > .../24_iterators/const_iterator/1.cc | 140 +++++++ > .../std/ranges/adaptors/as_const/1.cc | 64 +++ > .../testsuite/std/ranges/adaptors/join.cc | 5 +- > .../testsuite/std/ranges/version_c++23.cc | 4 + > 10 files changed, 824 insertions(+), 5 deletions(-) > create mode 100644 libstdc++-v3/testsuite/24_iterators/const_iterator/1.cc > create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/as_const/1.cc > >diff --git a/libstdc++-v3/include/bits/ranges_base.h b/libstdc++-v3/include/bits/ranges_base.h >index c89cb3e976a..b3144bbae4d 100644 >--- a/libstdc++-v3/include/bits/ranges_base.h >+++ b/libstdc++-v3/include/bits/ranges_base.h >@@ -515,6 +515,17 @@ namespace ranges > template > using sentinel_t = decltype(ranges::end(std::declval<_Range&>())); > >+#if __cplusplus > 202002L >+ template >+ using const_iterator_t = const_iterator>; >+ >+ template >+ using const_sentinel_t = const_sentinel>; >+ >+ template >+ using range_const_reference_t = iter_const_reference_t>; >+#endif >+ > template > using range_difference_t = iter_difference_t>; > >@@ -607,8 +618,25 @@ namespace ranges > concept common_range > = range<_Tp> && same_as, sentinel_t<_Tp>>; > >+#if __cplusplus > 202002L >+ template >+ concept constant_range >+ = input_range<_Tp> && std::__detail::__constant_iterator>; >+#endif >+ > namespace __cust_access > { >+#if __cplusplus > 202020L >+ template >+ constexpr auto& >+ __possibly_const_range(_Range& __r) noexcept >+ { >+ if constexpr (constant_range && !constant_range<_Range>) >+ return const_cast(__r); >+ else >+ return __r; >+ } >+#else > // If _To is an lvalue-reference, return const _Tp&, otherwise const _Tp&&. > template > constexpr decltype(auto) >@@ -621,9 +649,23 @@ namespace ranges > else > return static_cast(__t); > } >+#endif > > struct _CBegin > { >+#if __cplusplus > 202002L >+ template<__maybe_borrowed_range _Tp> >+ constexpr auto >+ operator() [[nodiscard]] (_Tp&& __t) const >+ noexcept(noexcept(std::make_const_iterator >+ (ranges::begin(__cust_access::__possibly_const_range(__t))))) >+ requires requires { std::make_const_iterator >+ (ranges::begin(__cust_access::__possibly_const_range(__t))); } Wow, that's an ugly declaration :-) >+ { >+ auto& __r = __cust_access::__possibly_const_range(__t); >+ return const_iterator_t(ranges::begin(__r)); >+ } >+#else > template > [[nodiscard]] > constexpr auto >@@ -633,10 +675,24 @@ namespace ranges > { > return _Begin{}(__cust_access::__as_const<_Tp>(__e)); > } >+#endif > }; > > struct _CEnd final > { >+#if __cplusplus > 202002L >+ template<__maybe_borrowed_range _Tp> >+ constexpr auto >+ operator() [[nodiscard]] (_Tp&& __t) const >+ noexcept(noexcept(std::make_const_sentinel >+ (ranges::end(__cust_access::__possibly_const_range(__t))))) >+ requires requires { std::make_const_sentinel >+ (ranges::end(__cust_access::__possibly_const_range(__t))); } >+ { >+ auto& __r = __cust_access::__possibly_const_range(__t); >+ return const_sentinel_t(ranges::end(__r)); >+ } >+#else > template > [[nodiscard]] > constexpr auto >@@ -646,10 +702,24 @@ namespace ranges > { > return _End{}(__cust_access::__as_const<_Tp>(__e)); > } >+#endif > }; > > struct _CRBegin > { >+#if __cplusplus > 202002L >+ template<__maybe_borrowed_range _Tp> >+ constexpr auto >+ operator() [[nodiscard]] (_Tp&& __t) const >+ noexcept(noexcept(std::make_const_iterator >+ (ranges::rbegin(__cust_access::__possibly_const_range(__t))))) >+ requires requires { std::make_const_iterator >+ (ranges::rbegin(__cust_access::__possibly_const_range(__t))); } >+ { >+ auto& __r = __cust_access::__possibly_const_range(__t); >+ return const_iterator(ranges::rbegin(__r)); >+ } >+#else > template > [[nodiscard]] > constexpr auto >@@ -659,10 +729,24 @@ namespace ranges > { > return _RBegin{}(__cust_access::__as_const<_Tp>(__e)); > } >+#endif > }; > > struct _CREnd > { >+#if __cplusplus > 202002L >+ template<__maybe_borrowed_range _Tp> >+ constexpr auto >+ operator() [[nodiscard]] (_Tp&& __t) const >+ noexcept(noexcept(std::make_const_sentinel >+ (ranges::rend(__cust_access::__possibly_const_range(__t))))) >+ requires requires { std::make_const_sentinel >+ (ranges::rend(__cust_access::__possibly_const_range(__t))); } >+ { >+ auto& __r = __cust_access::__possibly_const_range(__t); >+ return const_sentinel(ranges::rend(__r)); >+ } >+#else > template > [[nodiscard]] > constexpr auto >@@ -672,10 +756,24 @@ namespace ranges > { > return _REnd{}(__cust_access::__as_const<_Tp>(__e)); > } >+#endif > }; > > struct _CData > { >+#if __cplusplus > 202002L >+ template >+ static constexpr auto Would it be slightly cheaper to compile this if it returned const _Tp* instead of deducing the return type? >+ __as_const_pointer(const _Tp* __p) noexcept >+ { return __p; } >+ >+ template<__maybe_borrowed_range _Tp> >+ constexpr auto Do we even need __as_const_pointer or can we just return const auto* here? >+ operator() [[nodiscard]] (_Tp&& __t) const >+ noexcept(noexcept(ranges::data(__cust_access::__possibly_const_range(__t)))) >+ requires requires { ranges::data(__cust_access::__possibly_const_range(__t)); } >+ { return __as_const_pointer(ranges::data(__cust_access::__possibly_const_range(__t))); } >+#else > template > [[nodiscard]] > constexpr auto >@@ -685,6 +783,7 @@ namespace ranges > { > return _Data{}(__cust_access::__as_const<_Tp>(__e)); > } >+#endif > }; > > } // namespace __cust_access >diff --git a/libstdc++-v3/include/bits/ranges_util.h b/libstdc++-v3/include/bits/ranges_util.h >index 880a0ce0143..f7e3538af97 100644 >--- a/libstdc++-v3/include/bits/ranges_util.h >+++ b/libstdc++-v3/include/bits/ranges_util.h >@@ -53,9 +53,7 @@ namespace ranges > concept __has_arrow = input_iterator<_It> > && (is_pointer_v<_It> || requires(_It __it) { __it.operator->(); }); > >- template >- concept __different_from >- = !same_as, remove_cvref_t<_Up>>; >+ using std::__detail::__different_from; > } // namespace __detail > > /// The ranges::view_interface class template >@@ -192,6 +190,24 @@ namespace ranges > constexpr decltype(auto) > operator[](range_difference_t<_Range> __n) const > { return ranges::begin(_M_derived())[__n]; } >+ >+#if __cplusplus > 202002L >+ constexpr auto >+ cbegin() requires input_range<_Derived> >+ { return ranges::cbegin(_M_derived()); } >+ >+ constexpr auto >+ cbegin() const requires input_range >+ { return ranges::cbegin(_M_derived()); } >+ >+ constexpr auto >+ cend() requires input_range<_Derived> >+ { return ranges::cend(_M_derived()); } >+ >+ constexpr auto >+ cend() const requires input_range >+ { return ranges::cend(_M_derived()); } >+#endif > }; > > namespace __detail >diff --git a/libstdc++-v3/include/bits/stl_iterator.h b/libstdc++-v3/include/bits/stl_iterator.h >index a6a09dbac16..0974d735328 100644 >--- a/libstdc++-v3/include/bits/stl_iterator.h >+++ b/libstdc++-v3/include/bits/stl_iterator.h >@@ -102,6 +102,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > template > using __clamp_iter_cat > = __conditional_t, _Limit, _Otherwise>; >+ >+ template >+ concept __different_from >+ = !same_as, remove_cvref_t<_Up>>; > } > #endif > >@@ -2578,6 +2582,368 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > add_pointer_t>, > void>; > }; >+ >+#if __cplusplus > 202020L >+ template >+ using iter_const_reference_t >+ = common_reference_t&&, iter_reference_t<_It>>; >+ >+ template class basic_const_iterator; >+ >+ namespace __detail >+ { >+ template >+ concept __constant_iterator = input_iterator<_It> >+ && same_as, iter_reference_t<_It>>; >+ >+ template >+ inline constexpr bool __is_const_iterator = false; >+ >+ template >+ inline constexpr bool __is_const_iterator> = true; >+ >+ template >+ concept __not_a_const_iterator = !__is_const_iterator<_Tp>; >+ >+ template >+ using __iter_const_rvalue_reference_t >+ = common_reference_t&&, iter_rvalue_reference_t<_It>>; >+ >+ template >+ struct __basic_const_iterator_iter_cat >+ { }; >+ >+ template >+ struct __basic_const_iterator_iter_cat<_It> >+ { using iterator_category = iterator_traits<_It>::iterator_category; }; >+ } // namespace detail >+ >+ template >+ using const_iterator >+ = __conditional_t<__detail::__constant_iterator<_It>, _It, basic_const_iterator<_It>>; >+ >+ namespace __detail >+ { >+ template >+ struct __const_sentinel >+ { using type = _Sent; }; >+ >+ template >+ struct __const_sentinel<_Sent> >+ { using type = const_iterator<_Sent>; }; >+ } // namespace __detail >+ >+ template >+ using const_sentinel = typename __detail::__const_sentinel<_Sent>::type; >+ >+ template >+ class basic_const_iterator : public __detail::__basic_const_iterator_iter_cat<_It> Put the base-clause on a separate line please (I know we're being less strict these days about 80 columns max, but we don't need to do it here when we can just split it). All the other long lines seem OK though. OK with that change, and the changes to _CData if they make sense and I'm not missing something there. >+ { >+ _It _M_current = _It(); >+ using __reference = iter_const_reference_t<_It>; >+ using __rvalue_reference = __detail::__iter_const_rvalue_reference_t<_It>; >+ >+ static auto >+ _S_iter_concept() >+ { >+ if constexpr (contiguous_iterator<_It>) >+ return contiguous_iterator_tag{}; >+ else if constexpr (random_access_iterator<_It>) >+ return random_access_iterator_tag{}; >+ else if constexpr (bidirectional_iterator<_It>) >+ return bidirectional_iterator_tag{}; >+ else if constexpr (forward_iterator<_It>) >+ return forward_iterator_tag{}; >+ else >+ return input_iterator_tag{}; >+ } >+ >+ template friend class basic_const_iterator; >+ >+ public: >+ using iterator_concept = decltype(_S_iter_concept()); >+ using value_type = iter_value_t<_It>; >+ using difference_type = iter_difference_t<_It>; >+ >+ basic_const_iterator() requires default_initializable<_It> = default; >+ >+ constexpr >+ basic_const_iterator(_It __current) >+ noexcept(is_nothrow_move_constructible_v<_It>) >+ : _M_current(std::move(__current)) >+ { } >+ >+ template _It2> >+ constexpr >+ basic_const_iterator(basic_const_iterator<_It2> __current) >+ noexcept(is_nothrow_constructible_v<_It, _It2>) >+ : _M_current(std::move(__current._M_current)) >+ { } >+ >+ template<__detail::__different_from _Tp> >+ requires convertible_to<_Tp, _It> >+ constexpr >+ basic_const_iterator(_Tp&& __current) >+ noexcept(is_nothrow_constructible_v<_It, _Tp>) >+ : _M_current(std::forward<_Tp>(__current)) >+ { } >+ >+ constexpr const _It& >+ base() const & noexcept >+ { return _M_current; } >+ >+ constexpr _It >+ base() && >+ noexcept(is_nothrow_move_constructible_v<_It>) >+ { return std::move(_M_current); } >+ >+ constexpr __reference >+ operator*() const >+ noexcept(noexcept(static_cast<__reference>(*_M_current))) >+ { return static_cast<__reference>(*_M_current); } >+ >+ constexpr const auto* >+ operator->() const >+ noexcept(contiguous_iterator<_It> || noexcept(*_M_current)) >+ requires is_lvalue_reference_v> >+ && same_as>, value_type> >+ { >+ if constexpr (contiguous_iterator<_It>) >+ return std::to_address(_M_current); >+ else >+ return std::__addressof(*_M_current); >+ } >+ >+ constexpr basic_const_iterator& >+ operator++() >+ noexcept(noexcept(++_M_current)) >+ { >+ ++_M_current; >+ return *this; >+ } >+ >+ constexpr void >+ operator++(int) >+ noexcept(noexcept(++_M_current)) >+ { ++_M_current; } >+ >+ constexpr basic_const_iterator >+ operator++(int) >+ noexcept(noexcept(++*this) && is_nothrow_copy_constructible_v) >+ requires forward_iterator<_It> >+ { >+ auto __tmp = *this; >+ ++*this; >+ return __tmp; >+ } >+ >+ constexpr basic_const_iterator& >+ operator--() >+ noexcept(noexcept(--_M_current)) >+ requires bidirectional_iterator<_It> >+ { >+ --_M_current; >+ return *this; >+ } >+ >+ constexpr basic_const_iterator >+ operator--(int) >+ noexcept(noexcept(--*this) && is_nothrow_copy_constructible_v) >+ requires bidirectional_iterator<_It> >+ { >+ auto __tmp = *this; >+ --*this; >+ return __tmp; >+ } >+ >+ constexpr basic_const_iterator& >+ operator+=(difference_type __n) >+ noexcept(noexcept(_M_current += __n)) >+ requires random_access_iterator<_It> >+ { >+ _M_current += __n; >+ return *this; >+ } >+ >+ constexpr basic_const_iterator& >+ operator-=(difference_type __n) >+ noexcept(noexcept(_M_current -= __n)) >+ requires random_access_iterator<_It> >+ { >+ _M_current -= __n; >+ return *this; >+ } >+ >+ constexpr __reference >+ operator[](difference_type __n) const >+ noexcept(noexcept(static_cast<__reference>(_M_current[__n]))) >+ requires random_access_iterator<_It> >+ { return static_cast<__reference>(_M_current[__n]); } >+ >+ template _Sent> >+ constexpr bool >+ operator==(const _Sent& __s) const >+ noexcept(noexcept(_M_current == __s)) >+ { return _M_current == __s; } >+ >+ constexpr bool >+ operator<(const basic_const_iterator& __y) const >+ noexcept(noexcept(_M_current < __y._M_current)) >+ requires random_access_iterator<_It> >+ { return _M_current < __y._M_current; } >+ >+ constexpr bool >+ operator>(const basic_const_iterator& __y) const >+ noexcept(noexcept(_M_current > __y._M_current)) >+ requires random_access_iterator<_It> >+ { return _M_current > __y._M_current; } >+ >+ constexpr bool >+ operator<=(const basic_const_iterator& __y) const >+ noexcept(noexcept(_M_current <= __y._M_current)) >+ requires random_access_iterator<_It> >+ { return _M_current <= __y._M_current; } >+ >+ constexpr bool >+ operator>=(const basic_const_iterator& __y) const >+ noexcept(noexcept(_M_current >= __y._M_current)) >+ requires random_access_iterator<_It> >+ { return _M_current >= __y._M_current; } >+ >+ constexpr auto >+ operator<=>(const basic_const_iterator& __y) const >+ noexcept(noexcept(_M_current <=> __y._M_current)) >+ requires random_access_iterator<_It> && three_way_comparable<_It> >+ { return _M_current <=> __y._M_current; } >+ >+ template<__detail::__different_from _It2> >+ constexpr bool >+ operator<(const _It2& __y) const >+ noexcept(noexcept(_M_current < __y)) >+ requires random_access_iterator<_It> && totally_ordered_with<_It, _It2> >+ { return _M_current < __y; } >+ >+ template<__detail::__different_from _It2> >+ constexpr bool >+ operator>(const _It2& __y) const >+ noexcept(noexcept(_M_current > __y)) >+ requires random_access_iterator<_It> && totally_ordered_with<_It, _It2> >+ { return _M_current > __y; } >+ >+ template<__detail::__different_from _It2> >+ constexpr bool >+ operator<=(const _It2& __y) const >+ noexcept(noexcept(_M_current <= __y)) >+ requires random_access_iterator<_It> && totally_ordered_with<_It, _It2> >+ { return _M_current <= __y; } >+ >+ template<__detail::__different_from _It2> >+ constexpr bool >+ operator>=(const _It2& __y) const >+ noexcept(noexcept(_M_current >= __y)) >+ requires random_access_iterator<_It> && totally_ordered_with<_It, _It2> >+ { return _M_current >= __y; } >+ >+ template<__detail::__different_from _It2> >+ constexpr auto >+ operator<=>(const _It2& __y) const >+ noexcept(noexcept(_M_current <=> __y)) >+ requires random_access_iterator<_It> && totally_ordered_with<_It, _It2> >+ && three_way_comparable_with<_It, _It2> >+ { return _M_current <=> __y; } >+ >+ template<__detail::__not_a_const_iterator _It2> >+ friend constexpr bool >+ operator<(const _It2& __x, const basic_const_iterator& __y) >+ noexcept(noexcept(__x < __y._M_current)) >+ requires random_access_iterator<_It> && totally_ordered_with<_It, _It2> >+ { return __x < __y._M_current; } >+ >+ template<__detail::__not_a_const_iterator _It2> >+ friend constexpr bool >+ operator>(const _It2& __x, const basic_const_iterator& __y) >+ noexcept(noexcept(__x > __y._M_current)) >+ requires random_access_iterator<_It> && totally_ordered_with<_It, _It2> >+ { return __x > __y._M_current; } >+ >+ template<__detail::__not_a_const_iterator _It2> >+ friend constexpr bool >+ operator<=(const _It2& __x, const basic_const_iterator& __y) >+ noexcept(noexcept(__x <= __y._M_current)) >+ requires random_access_iterator<_It> && totally_ordered_with<_It, _It2> >+ { return __x <= __y._M_current; } >+ >+ template<__detail::__not_a_const_iterator _It2> >+ friend constexpr bool >+ operator>=(const _It2& __x, const basic_const_iterator& __y) >+ noexcept(noexcept(__x >= __y._M_current)) >+ requires random_access_iterator<_It> && totally_ordered_with<_It, _It2> >+ { return __x >= __y._M_current; } >+ >+ friend constexpr basic_const_iterator >+ operator+(const basic_const_iterator& __i, difference_type __n) >+ noexcept(noexcept(basic_const_iterator(__i._M_current + __n))) >+ requires random_access_iterator<_It> >+ { return basic_const_iterator(__i._M_current + __n); } >+ >+ friend constexpr basic_const_iterator >+ operator+(difference_type __n, const basic_const_iterator& __i) >+ noexcept(noexcept(basic_const_iterator(__i._M_current + __n))) >+ requires random_access_iterator<_It> >+ { return basic_const_iterator(__i._M_current + __n); } >+ >+ friend constexpr basic_const_iterator >+ operator-(const basic_const_iterator& __i, difference_type __n) >+ noexcept(noexcept(basic_const_iterator(__i._M_current - __n))) >+ requires random_access_iterator<_It> >+ { return basic_const_iterator(__i._M_current - __n); } >+ >+ template _Sent> >+ constexpr difference_type >+ operator-(const _Sent& __y) const >+ noexcept(noexcept(_M_current - __y)) >+ { return _M_current - __y; } >+ >+ template<__detail::__not_a_const_iterator _Sent> >+ requires sized_sentinel_for<_Sent, _It> >+ friend constexpr difference_type >+ operator-(const _Sent& __x, const basic_const_iterator& __y) >+ noexcept(noexcept(__x - __y._M_current)) >+ { return __x - __y._M_current; } >+ >+ friend constexpr __rvalue_reference >+ iter_move(const basic_const_iterator& __i) >+ noexcept(noexcept(static_cast<__rvalue_reference>(ranges::iter_move(__i._M_current)))) >+ { return static_cast<__rvalue_reference>(ranges::iter_move(__i._M_current)); } >+ }; >+ >+ template _Up> >+ requires input_iterator> >+ struct common_type, _Up> >+ { using type = basic_const_iterator>; }; >+ >+ template _Up> >+ requires input_iterator> >+ struct common_type<_Up, basic_const_iterator<_Tp>> >+ { using type = basic_const_iterator>; }; >+ >+ template _Up> >+ requires input_iterator> >+ struct common_type, basic_const_iterator<_Up>> >+ { using type = basic_const_iterator>; }; >+ >+ template >+ constexpr const_iterator<_It> >+ make_const_iterator(_It __it) >+ noexcept(is_nothrow_convertible_v<_It, const_iterator<_It>>) >+ { return __it; } >+ >+ template >+ constexpr const_sentinel<_Sent> >+ make_const_sentinel(_Sent __s) >+ noexcept(is_nothrow_convertible_v<_Sent, const_sentinel<_Sent>>) >+ { return __s; } >+#endif // C++23 > #endif // C++20 > > /// @} group iterators >diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges >index 3f6ff505617..283d757faa4 100644 >--- a/libstdc++-v3/include/std/ranges >+++ b/libstdc++-v3/include/std/ranges >@@ -8929,6 +8929,112 @@ namespace views::__adaptor > > inline constexpr _Enumerate enumerate; > } >+ >+#define __cpp_lib_ranges_as_const 202207L >+ >+ template >+ requires input_range<_Vp> >+ class as_const_view : public view_interface> >+ { >+ _Vp _M_base = _Vp(); >+ >+ public: >+ as_const_view() requires default_initializable<_Vp> = default; >+ >+ constexpr explicit >+ as_const_view(_Vp __base) >+ noexcept(is_nothrow_move_constructible_v<_Vp>) >+ : _M_base(std::move(__base)) >+ { } >+ >+ constexpr _Vp >+ base() const & >+ noexcept(is_nothrow_copy_constructible_v<_Vp>) >+ requires copy_constructible<_Vp> >+ { return _M_base; } >+ >+ constexpr _Vp >+ base() && >+ noexcept(is_nothrow_move_constructible_v<_Vp>) >+ { return std::move(_M_base); } >+ >+ constexpr auto >+ begin() requires (!__detail::__simple_view<_Vp>) >+ { return ranges::cbegin(_M_base); } >+ >+ constexpr auto >+ begin() const requires range >+ { return ranges::cbegin(_M_base); } >+ >+ constexpr auto >+ end() requires (!__detail::__simple_view<_Vp>) >+ { return ranges::cend(_M_base); } >+ >+ constexpr auto >+ end() const requires range >+ { return ranges::cend(_M_base); } >+ >+ constexpr auto >+ size() requires sized_range<_Vp> >+ { return ranges::size(_M_base); } >+ >+ constexpr auto >+ size() const requires sized_range >+ { return ranges::size(_M_base); } >+ }; >+ >+ template >+ as_const_view(_Range&&) -> as_const_view>; >+ >+ template >+ inline constexpr bool enable_borrowed_range> >+ = enable_borrowed_range<_Tp>; >+ >+ namespace views >+ { >+ namespace __detail >+ { >+ template >+ inline constexpr bool __is_ref_view = false; >+ >+ template >+ inline constexpr bool __is_ref_view> = true; >+ >+ template >+ concept __can_as_const_view = requires { as_const_view(std::declval<_Range>()); }; >+ } >+ >+ struct _AsConst : __adaptor::_RangeAdaptorClosure >+ { >+ template >+ constexpr auto >+ operator()(_Range&& __r) const >+ noexcept(noexcept(as_const_view(std::declval<_Range>()))) >+ requires __detail::__can_as_const_view<_Range> >+ { >+ using _Tp = remove_cvref_t<_Range>; >+ using element_type = remove_reference_t>; >+ if constexpr (constant_range>) >+ return views::all(std::forward<_Range>(__r)); >+ else if constexpr (__detail::__is_empty_view<_Tp>) >+ return views::empty; >+ else if constexpr (std::__detail::__is_span<_Tp>) >+ return span(std::forward<_Range>(__r)); >+ else if constexpr (__detail::__is_ref_view<_Tp> >+ && constant_range) >+ return ref_view(static_cast >+ (std::forward<_Range>(__r).base())); >+ else if constexpr (is_lvalue_reference_v<_Range> >+ && constant_range<_Tp> >+ && !view<_Tp>) >+ return ref_view(static_cast(__r)); >+ else >+ return as_const_view(std::forward<_Range>(__r)); >+ } >+ }; >+ >+ inline constexpr _AsConst as_const; >+ } > #endif // C++23 > } // namespace ranges > >diff --git a/libstdc++-v3/include/std/span b/libstdc++-v3/include/std/span >index 06d5c185880..67633899665 100644 >--- a/libstdc++-v3/include/std/span >+++ b/libstdc++-v3/include/std/span >@@ -137,6 +137,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > using const_reference = const element_type&; > using iterator = __gnu_cxx::__normal_iterator; > using reverse_iterator = std::reverse_iterator; >+#if __cplusplus > 202002L >+ using const_iterator = std::const_iterator; >+ using const_reverse_iterator = std::const_iterator; >+#endif > > // member constants > static constexpr size_t extent = _Extent; >@@ -301,6 +305,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > rend() const noexcept > { return reverse_iterator(this->begin()); } > >+#if __cplusplus > 202002L >+ constexpr const_iterator >+ cbegin() const noexcept >+ { return begin(); } >+ >+ constexpr const_iterator >+ cend() const noexcept >+ { return end(); } >+ >+ constexpr const_reverse_iterator >+ crbegin() const noexcept >+ { return rbegin(); } >+ >+ constexpr const_reverse_iterator >+ crend() const noexcept >+ { return rend(); } >+#endif >+ > // subviews > > template >diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version >index d233b037d1a..9f31f25f1e9 100644 >--- a/libstdc++-v3/include/std/version >+++ b/libstdc++-v3/include/std/version >@@ -339,6 +339,7 @@ > #define __cpp_lib_ranges_stride 202207L > #define __cpp_lib_ranges_cartesian_product 202207L > #define __cpp_lib_ranges_as_rvalue 202207L >+#define __cpp_lib_ranges_as_const 202207L > #define __cpp_lib_ranges_enumerate 202302L > #define __cpp_lib_fold 202207L > #if __cpp_constexpr_dynamic_alloc >diff --git a/libstdc++-v3/testsuite/24_iterators/const_iterator/1.cc b/libstdc++-v3/testsuite/24_iterators/const_iterator/1.cc >new file mode 100644 >index 00000000000..51befd29541 >--- /dev/null >+++ b/libstdc++-v3/testsuite/24_iterators/const_iterator/1.cc >@@ -0,0 +1,140 @@ >+// { dg-options "-std=gnu++23" } >+// { dg-do run { target c++23 } } >+ >+#include >+#include >+#include >+#include >+#include >+#include >+ >+using __gnu_test::test_input_range; >+using __gnu_test::test_forward_range; >+using __gnu_test::test_bidirectional_range; >+using __gnu_test::test_random_access_range; >+ >+namespace ranges = std::ranges; >+ >+template >+void >+test01() >+{ >+ if constexpr (Const) >+ { >+ static_assert( std::same_as, Iter> ); >+ static_assert( std::same_as, Iter> ); >+ static_assert( std::same_as, >+ std::iter_reference_t> ); >+ } >+ else >+ { >+ using Wrapped = std::basic_const_iterator; >+ >+ static_assert( std::same_as, Wrapped> ); >+ static_assert( std::same_as, Wrapped> ); >+ static_assert( std::same_as, >+ std::iter_reference_t> ); >+ >+ static_assert( std::input_iterator == std::input_iterator ); >+ static_assert( std::forward_iterator == std::forward_iterator ); >+ static_assert( std::bidirectional_iterator == std::bidirectional_iterator ); >+ static_assert( std::random_access_iterator == std::random_access_iterator ); >+ } >+} >+ >+template >+void >+test02() >+{ >+ if constexpr (Const) >+ { >+ static_assert( ranges::constant_range ); >+ static_assert( std::same_as, ranges::iterator_t> ); >+ static_assert( std::same_as, ranges::sentinel_t> ); >+ static_assert( std::same_as, >+ ranges::range_reference_t> ); >+ >+ static_assert( std::same_as())), >+ decltype(ranges::begin(std::declval()))> ); >+ static_assert( std::same_as())), >+ decltype(ranges::end(std::declval()))> ); >+ } >+ else >+ { >+ static_assert( !ranges::constant_range ); >+ using Wrapped = std::basic_const_iterator>; >+ >+ static_assert( std::same_as, Wrapped> ); >+ if constexpr (ranges::common_range) >+ static_assert( std::same_as, Wrapped> ); >+ static_assert( std::same_as, >+ std::iter_reference_t> ); >+ >+ static_assert( ranges::input_range == std::input_iterator ); >+ static_assert( ranges::forward_range == std::forward_iterator ); >+ static_assert( ranges::bidirectional_range == std::bidirectional_iterator ); >+ static_assert( ranges::random_access_range == std::random_access_iterator ); >+ >+ if constexpr (ranges::constant_range) >+ { >+ static_assert( std::same_as())), >+ decltype(ranges::begin(std::declval()))> ); >+ static_assert( std::same_as())), >+ decltype(ranges::end(std::declval()))> ); >+ } >+ else >+ { >+ static_assert( std::same_as())), Wrapped> ); >+ if constexpr (ranges::common_range) >+ static_assert( std::same_as())), Wrapped> ); >+ } >+ } >+} >+ >+void >+test03() >+{ >+ static_assert( std::same_as, >+ std::unreachable_sentinel_t> ); >+} >+ >+int >+main() >+{ >+ test01(); >+ test01>, false>(); >+ test01>, false>(); >+ test01>, false>(); >+ test01>, false>(); >+ test01::iterator, false>(); >+ test01::iterator, false>(); >+ >+ test01(); >+ test01>, true>(); >+ test01>, true>(); >+ test01>, true>(); >+ test01>, true>(); >+ test01::iterator, true>(); >+ test01(); >+ test01::const_iterator, true>(); >+ >+ test02(); >+ test02, false>(); >+ test02, false>(); >+ test02, false>(); >+ test02, false>(); >+ test02, false>(); >+ test02, false>(); >+ >+ test02(); >+ test02, true>(); >+ test02, true>(); >+ test02, true>(); >+ test02, true>(); >+ test02, true>(); >+ test02, true>(); >+ test02(); >+ test02, true>(); >+ >+ test03(); >+} >diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/as_const/1.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/as_const/1.cc >new file mode 100644 >index 00000000000..d04645f047e >--- /dev/null >+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/as_const/1.cc >@@ -0,0 +1,64 @@ >+// { dg-options "-std=gnu++23" } >+// { dg-do run { target c++23 } } >+ >+#include >+#include >+#include >+#include >+#include >+#include >+ >+#if __cpp_lib_ranges_as_const != 202207L >+# error "Feature-test macro __cpp_lib_ranges_as_const has wrong value in " >+#endif >+ >+namespace ranges = std::ranges; >+namespace views = std::views; >+ >+constexpr bool >+test01() >+{ >+ int x[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; >+ auto v = x | views::filter([](int x) { return (x % 2) == 0; }) | views::as_const; >+ >+ using ty = decltype(v); >+ static_assert(ranges::constant_range); >+ static_assert(!ranges::constant_range); >+ static_assert(std::same_as, const int&>); >+ static_assert(std::same_as, int&>); >+ >+ VERIFY( ranges::equal(v, (int[]){2, 4, 6, 8, 10}) ); >+ VERIFY( ranges::equal(v | views::reverse, (int[]){10, 8, 6, 4, 2}) ); >+ >+ return true; >+} >+ >+constexpr bool >+test02() >+{ >+ std::same_as> auto v1 >+ = views::empty | views::as_const; >+ >+ int x[] = {1, 2, 3}; >+ std::same_as>> auto v2 >+ = x | views::as_const; >+ std::same_as> auto v3 >+ = std::as_const(x) | views::as_const; >+ std::same_as> auto v4 >+ = std::as_const(x) | views::all | views::as_const; >+ std::same_as> auto v5 >+ = std::span{x, x+3} | views::as_const; >+ >+ std::same_as>>> auto v6 >+ = x | views::chunk(2) | views::as_const; >+ VERIFY( v6.size() == 2 ); >+ >+ return true; >+} >+ >+int >+main() >+{ >+ static_assert(test01()); >+ static_assert(test02()); >+} >diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc >index e5ae9b7be20..cda2cbfc577 100644 >--- a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc >+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc >@@ -24,6 +24,7 @@ > #include > #include > #include >+#include > #include > #include > #include >@@ -120,8 +121,8 @@ test06() > // Verify that _Sentinel is implicitly convertible to _Sentinel. > static_assert(!ranges::common_range); > static_assert(!std::same_as- decltype(ranges::cend(v))>); >- auto b = ranges::cend(v); >+ decltype(std::as_const(v).end())>); >+ auto b = std::as_const(v).end(); > b = ranges::end(v); > } > >diff --git a/libstdc++-v3/testsuite/std/ranges/version_c++23.cc b/libstdc++-v3/testsuite/std/ranges/version_c++23.cc >index fa010bf166b..e2c14edc8ef 100644 >--- a/libstdc++-v3/testsuite/std/ranges/version_c++23.cc >+++ b/libstdc++-v3/testsuite/std/ranges/version_c++23.cc >@@ -45,6 +45,10 @@ > # error "Feature-test macro __cpp_lib_ranges_as_rvalue has wrong value in " > #endif > >+#if __cpp_lib_ranges_as_const != 202207L >+# error "Feature-test macro __cpp_lib_ranges_as_const has wrong value in " >+#endif >+ > #if __cpp_lib_ranges_enumerate != 202302L > # error "Feature-test macro __cpp_lib_ranges_enumerate has wrong value in " > #endif