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 38DB23858D20 for ; Fri, 14 Apr 2023 14:14:36 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 38DB23858D20 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=1681481675; 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=TXlW40uhUIU7+JGAU3yX3lBswEiOdrc7yOG5uIMPcrw=; b=d+Nos4QgU9YlzX3mij7Le4btl2Ne/tkLS1UlLSNXIAX0kLsH33NEYsGVQIc8rf82jhHyqO aiOLB2bptVfc+iM0zjurdT+ciAOVJL+QC0xgIoLtT2q76+nsjmiLhzNjdNQKeGFtFOqcEd fuDGSVzacuuXQ9+F/FyJSMTYdo+klsE= Received: from mail-qt1-f198.google.com (mail-qt1-f198.google.com [209.85.160.198]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-674-DvQdadPUPkCi3xb81xEMQQ-1; Fri, 14 Apr 2023 10:14:34 -0400 X-MC-Unique: DvQdadPUPkCi3xb81xEMQQ-1 Received: by mail-qt1-f198.google.com with SMTP id e8-20020a05622a110800b003e4e915a164so8769441qty.4 for ; Fri, 14 Apr 2023 07:14:34 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681481674; x=1684073674; h=mime-version:references:message-id:in-reply-to:subject:cc:to:date :from:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=TXlW40uhUIU7+JGAU3yX3lBswEiOdrc7yOG5uIMPcrw=; b=ilYPJIewGrb1jslejJ2zFYj8UfH39asIN1KwON6rX40mwNoHw7S/jPMxZ46yfCtugx VgN4OE2Pc2snnf9Yp3RUjHIIWDvr3bFLoimNa8iT71cy2n+SDeoZ1Pa66lSR8s5XLt6T pYBmPSRezFEyVVi3aID3vZZJ/oKXTPfrRIVYsnm1tVJqA5DTsTnEkP5p/5wTevdXCJcz +kuq08N6F9lASyy91plkcVhN0Q4l7M7wRGbYUVihHBzDchgSxhufhD3Ximyvl3Ci2kQU vYx8cHvTcP9L8lVSAq0L43o/pTiqGPEcDL+IHkWA/q3gl5siXuQ8VzD1nMTG2LlMFxvF Wulw== X-Gm-Message-State: AAQBX9eg3E/qkNkyHj2L/xpX4OqoouAf/YbrMroE6T3XAjsBNVhHi2II 56AdxbSvbgRonglUuGe/pOkKaFNSa6ikkWvcy+O9N49wzfcjIC+wmV/4DQNxFmdnIt09+Vf5SI8 nge4o7HOOll9Ym/JRlGERG5Fy4w== X-Received: by 2002:ac8:5fcc:0:b0:3eb:8f6a:a11 with SMTP id k12-20020ac85fcc000000b003eb8f6a0a11mr2333007qta.16.1681481673653; Fri, 14 Apr 2023 07:14:33 -0700 (PDT) X-Google-Smtp-Source: AKy350aDzyshNqvieHwS12XUcWxIV2D061Yy/FWIups1JEbhI+hEdtL5Ue903IwWyU8nTYdXpgsmog== X-Received: by 2002:ac8:5fcc:0:b0:3eb:8f6a:a11 with SMTP id k12-20020ac85fcc000000b003eb8f6a0a11mr2332943qta.16.1681481673046; Fri, 14 Apr 2023 07:14:33 -0700 (PDT) Received: from [192.168.1.130] (ool-457670bb.dyn.optonline.net. [69.118.112.187]) by smtp.gmail.com with ESMTPSA id bm38-20020a05620a19a600b007422eee8058sm1234187qkb.125.2023.04.14.07.14.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 14 Apr 2023 07:14:32 -0700 (PDT) From: Patrick Palka X-Google-Original-From: Patrick Palka Date: Fri, 14 Apr 2023 10:14:31 -0400 (EDT) To: Jonathan Wakely cc: Patrick Palka , gcc-patches@gcc.gnu.org, libstdc++@gcc.gnu.org Subject: Re: [PATCH 2/2] libstdc++: Implement P2278R4 "cbegin should always return a constant iterator" In-Reply-To: Message-ID: <6af61d9e-1afc-688a-0c4e-be74b5313506@idea> References: <20230414040042.1498825-1-ppalka@redhat.com> <20230414040042.1498825-2-ppalka@redhat.com> MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset=US-ASCII X-Spam-Status: No, score=-13.9 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=ham 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 Fri, 14 Apr 2023, Jonathan Wakely wrote: > 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 :-) Yeah... though I'm hopeful it's possible to at least simplify these CPO constraints substantially (at the expense of making them less clearly correct). For example cbegin's constraint requires { std::make_const_iterator (ranges::begin(__cust_access::__possibly_const_range(__t))); } is probably equivalent to just input_iterator>. And cend's constraint requires { std::make_const_sentinel (ranges::end(__cust_access::__possibly_const_range(__t))); } is probably equivalent to just requires { ranges::end(__t); }. I can attempt simplifying them in a follow-up patch. > > > + { > > + 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? Ah, that's a nice simplification, I didn't consider getting rid of __as_const_pointer. We know that ranges::data always returns a pointer type, so deducing const auto* here should always succeed. > > > + 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. Done. > > OK with that change, and the changes to _CData if they make sense and > I'm not missing something there. Thanks! Will commit shortly with those changes. > > > + { > > + _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 > _Tp::extent>(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 > Range&>()))> ); > > + static_assert( > > std::same_as())), > > + decltype(ranges::end(std::declval > Range&>()))> ); > > + } > > + 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 > >