public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Patrick Palka <ppalka@redhat.com>
To: Jonathan Wakely <jwakely@redhat.com>
Cc: Patrick Palka <ppalka@redhat.com>,
	gcc-patches@gcc.gnu.org,  libstdc++@gcc.gnu.org
Subject: Re: [PATCH 2/2] libstdc++: Implement P2278R4 "cbegin should always return a constant iterator"
Date: Fri, 14 Apr 2023 10:14:31 -0400 (EDT)	[thread overview]
Message-ID: <6af61d9e-1afc-688a-0c4e-be74b5313506@idea> (raw)
In-Reply-To: <ZDknezDIyFTNcNkE@redhat.com>

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<basic_const_iterator<_Tp>, _Up>): Likewise.
> > 	(common_type<_Up, basic_const_iterator<_Tp>>): Likewise.
> > 	(common_type<basic_const_iterator<_Tp>, basic_const_iterator<Up>>):
> > 	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<as_const_view>): 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<range _Range>
> >     using sentinel_t = decltype(ranges::end(std::declval<_Range&>()));
> > 
> > +#if __cplusplus > 202002L
> > +  template<range _Range>
> > +    using const_iterator_t = const_iterator<iterator_t<_Range>>;
> > +
> > +  template<range _Range>
> > +    using const_sentinel_t = const_sentinel<sentinel_t<_Range>>;
> > +
> > +  template<range _Range>
> > +    using range_const_reference_t =
> > iter_const_reference_t<iterator_t<_Range>>;
> > +#endif
> > +
> >   template<range _Range>
> >     using range_difference_t = iter_difference_t<iterator_t<_Range>>;
> > 
> > @@ -607,8 +618,25 @@ namespace ranges
> >     concept common_range
> >       = range<_Tp> && same_as<iterator_t<_Tp>, sentinel_t<_Tp>>;
> > 
> > +#if __cplusplus > 202002L
> > +  template<typename _Tp>
> > +    concept constant_range
> > +      = input_range<_Tp> &&
> > std::__detail::__constant_iterator<iterator_t<_Tp>>;
> > +#endif
> > +
> >   namespace __cust_access
> >   {
> > +#if __cplusplus > 202020L
> > +    template<typename _Range>
> > +      constexpr auto&
> > +      __possibly_const_range(_Range& __r) noexcept
> > +      {
> > +	if constexpr (constant_range<const _Range> && !constant_range<_Range>)
> > +	  return const_cast<const _Range&>(__r);
> > +	else
> > +	  return __r;
> > +      }
> > +#else
> >     // If _To is an lvalue-reference, return const _Tp&, otherwise const
> > _Tp&&.
> >     template<typename _To, typename _Tp>
> >       constexpr decltype(auto)
> > @@ -621,9 +649,23 @@ namespace ranges
> > 	else
> > 	  return static_cast<const _Tp&&>(__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<iterator_t<_Tp>>.  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<decltype(__r)>(ranges::begin(__r));
> > +	}
> > +#else
> >       template<typename _Tp>
> > 	[[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<decltype(__r)>(ranges::end(__r));
> > +	}
> > +#else
> >       template<typename _Tp>
> > 	[[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<decltype(ranges::rbegin(__r))>(ranges::rbegin(__r));
> > +	}
> > +#else
> >       template<typename _Tp>
> > 	[[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<decltype(ranges::rend(__r))>(ranges::rend(__r));
> > +	}
> > +#else
> >       template<typename _Tp>
> > 	[[nodiscard]]
> > 	constexpr auto
> > @@ -672,10 +756,24 @@ namespace ranges
> > 	{
> > 	  return _REnd{}(__cust_access::__as_const<_Tp>(__e));
> > 	}
> > +#endif
> >     };
> > 
> >     struct _CData
> >     {
> > +#if __cplusplus > 202002L
> > +      template<typename _Tp>
> > +	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<typename _Tp>
> > 	[[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<typename _Tp, typename _Up>
> > -      concept __different_from
> > -	= !same_as<remove_cvref_t<_Tp>, 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<const _Derived>
> > +      { return ranges::cbegin(_M_derived()); }
> > +
> > +      constexpr auto
> > +      cend() requires input_range<_Derived>
> > +      { return ranges::cend(_M_derived()); }
> > +
> > +      constexpr auto
> > +      cend() const requires input_range<const _Derived>
> > +      { 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<typename _Cat, typename _Limit, typename _Otherwise = _Cat>
> >       using __clamp_iter_cat
> > 	= __conditional_t<derived_from<_Cat, _Limit>, _Limit, _Otherwise>;
> > +
> > +    template<typename _Tp, typename _Up>
> > +      concept __different_from
> > +	= !same_as<remove_cvref_t<_Tp>, remove_cvref_t<_Up>>;
> >   }
> > #endif
> > 
> > @@ -2578,6 +2582,368 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > 				      add_pointer_t<iter_reference_t<_It>>,
> > 				      void>;
> >     };
> > +
> > +#if __cplusplus > 202020L
> > +  template<indirectly_readable _It>
> > +    using iter_const_reference_t
> > +      = common_reference_t<const iter_value_t<_It>&&,
> > iter_reference_t<_It>>;
> > +
> > +  template<input_iterator _It> class basic_const_iterator;
> > +
> > +  namespace __detail
> > +  {
> > +    template<typename _It>
> > +      concept __constant_iterator = input_iterator<_It>
> > +	&& same_as<iter_const_reference_t<_It>, iter_reference_t<_It>>;
> > +
> > +    template<typename _Tp>
> > +      inline constexpr bool __is_const_iterator = false;
> > +
> > +    template<typename _It>
> > +      inline constexpr bool __is_const_iterator<basic_const_iterator<_It>>
> > = true;
> > +
> > +    template<typename _Tp>
> > +      concept __not_a_const_iterator = !__is_const_iterator<_Tp>;
> > +
> > +    template<indirectly_readable _It>
> > +      using __iter_const_rvalue_reference_t
> > +	= common_reference_t<const iter_value_t<_It>&&,
> > iter_rvalue_reference_t<_It>>;
> > +
> > +    template<typename _It>
> > +      struct __basic_const_iterator_iter_cat
> > +      { };
> > +
> > +    template<forward_iterator _It>
> > +      struct __basic_const_iterator_iter_cat<_It>
> > +      { using iterator_category = iterator_traits<_It>::iterator_category;
> > };
> > +  } // namespace detail
> > +
> > +  template<input_iterator _It>
> > +    using const_iterator
> > +      = __conditional_t<__detail::__constant_iterator<_It>, _It,
> > basic_const_iterator<_It>>;
> > +
> > +  namespace __detail
> > +  {
> > +    template<typename _Sent>
> > +      struct __const_sentinel
> > +      { using type = _Sent; };
> > +
> > +    template<input_iterator _Sent>
> > +      struct __const_sentinel<_Sent>
> > +      { using type = const_iterator<_Sent>; };
> > +  } // namespace __detail
> > +
> > +  template<semiregular _Sent>
> > +    using const_sentinel = typename
> > __detail::__const_sentinel<_Sent>::type;
> > +
> > +  template<input_iterator _It>
> > +  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<input_iterator _It2> 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<convertible_to<_It> _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<basic_const_iterator> _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<iter_reference_t<_It>>
> > +      && same_as<remove_cvref_t<iter_reference_t<_It>>, 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<basic_const_iterator>)
> > +    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<basic_const_iterator>)
> > +    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<sentinel_for<_It> _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<basic_const_iterator> _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<basic_const_iterator> _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<basic_const_iterator> _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<basic_const_iterator> _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<basic_const_iterator> _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<sized_sentinel_for<_It> _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<typename _Tp, common_with<_Tp> _Up>
> > +    requires input_iterator<common_type_t<_Tp, _Up>>
> > +    struct common_type<basic_const_iterator<_Tp>, _Up>
> > +    { using type = basic_const_iterator<common_type_t<_Tp, _Up>>; };
> > +
> > +  template<typename _Tp, common_with<_Tp> _Up>
> > +    requires input_iterator<common_type_t<_Tp, _Up>>
> > +    struct common_type<_Up, basic_const_iterator<_Tp>>
> > +    { using type = basic_const_iterator<common_type_t<_Tp, _Up>>; };
> > +
> > +  template<typename _Tp, common_with<_Tp> _Up>
> > +    requires input_iterator<common_type_t<_Tp, _Up>>
> > +    struct common_type<basic_const_iterator<_Tp>,
> > basic_const_iterator<_Up>>
> > +    { using type = basic_const_iterator<common_type_t<_Tp, _Up>>; };
> > +
> > +  template<input_iterator _It>
> > +    constexpr const_iterator<_It>
> > +    make_const_iterator(_It __it)
> > +    noexcept(is_nothrow_convertible_v<_It, const_iterator<_It>>)
> > +    { return __it; }
> > +
> > +  template<semiregular _Sent>
> > +    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<view _Vp>
> > +    requires input_range<_Vp>
> > +  class as_const_view : public view_interface<as_const_view<_Vp>>
> > +  {
> > +    _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<const _Vp>
> > +    { return ranges::cbegin(_M_base); }
> > +
> > +    constexpr auto
> > +    end() requires (!__detail::__simple_view<_Vp>)
> > +    { return ranges::cend(_M_base); }
> > +
> > +    constexpr auto
> > +    end() const requires range<const _Vp>
> > +    { return ranges::cend(_M_base); }
> > +
> > +    constexpr auto
> > +    size() requires sized_range<_Vp>
> > +    { return ranges::size(_M_base); }
> > +
> > +    constexpr auto
> > +    size() const requires sized_range<const _Vp>
> > +    { return ranges::size(_M_base); }
> > +  };
> > +
> > +  template<typename _Range>
> > +    as_const_view(_Range&&) -> as_const_view<views::all_t<_Range>>;
> > +
> > +  template<typename _Tp>
> > +    inline constexpr bool enable_borrowed_range<as_const_view<_Tp>>
> > +      = enable_borrowed_range<_Tp>;
> > +
> > +  namespace views
> > +  {
> > +    namespace __detail
> > +    {
> > +      template<typename _Tp>
> > +	inline constexpr bool __is_ref_view = false;
> > +
> > +      template<typename _Range>
> > +	inline constexpr bool __is_ref_view<ref_view<_Range>> = true;
> > +
> > +      template<typename _Range>
> > +	concept __can_as_const_view = requires {
> > as_const_view(std::declval<_Range>()); };
> > +    }
> > +
> > +    struct _AsConst : __adaptor::_RangeAdaptorClosure
> > +    {
> > +      template<viewable_range _Range>
> > +      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<range_reference_t<_Range>>;
> > +	if constexpr (constant_range<views::all_t<_Range>>)
> > +	  return views::all(std::forward<_Range>(__r));
> > +	else if constexpr (__detail::__is_empty_view<_Tp>)
> > +	  return views::empty<const element_type>;
> > +	else if constexpr (std::__detail::__is_span<_Tp>)
> > +	  return span<const element_type,
> > _Tp::extent>(std::forward<_Range>(__r));
> > +	else if constexpr (__detail::__is_ref_view<_Tp>
> > +			   && constant_range<const element_type>)
> > +	  return ref_view(static_cast<const element_type&>
> > +			  (std::forward<_Range>(__r).base()));
> > +	else if constexpr (is_lvalue_reference_v<_Range>
> > +			   && constant_range<_Tp>
> > +			   && !view<_Tp>)
> > +	  return ref_view(static_cast<const _Tp&>(__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<pointer, span>;
> >       using reverse_iterator       = std::reverse_iterator<iterator>;
> > +#if __cplusplus > 202002L
> > +      using const_iterator         = std::const_iterator<iterator>;
> > +      using const_reverse_iterator = std::const_iterator<reverse_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<size_t _Count>
> > 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 <iterator>
> > +#include <array>
> > +#include <concepts>
> > +#include <string_view>
> > +#include <vector>
> > +#include <testsuite_iterators.h>
> > +
> > +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<class Iter, bool Const>
> > +void
> > +test01()
> > +{
> > +  if constexpr (Const)
> > +    {
> > +      static_assert( std::same_as<std::const_iterator<Iter>, Iter> );
> > +      static_assert( std::same_as<std::const_sentinel<Iter>, Iter> );
> > +      static_assert( std::same_as<std::iter_const_reference_t<Iter>,
> > +				 std::iter_reference_t<Iter>> );
> > +    }
> > +  else
> > +    {
> > +      using Wrapped = std::basic_const_iterator<Iter>;
> > +
> > +      static_assert( std::same_as<std::const_iterator<Iter>, Wrapped> );
> > +      static_assert( std::same_as<std::const_sentinel<Iter>, Wrapped> );
> > +      static_assert( std::same_as<std::iter_const_reference_t<Iter>,
> > +				 std::iter_reference_t<Wrapped>> );
> > +
> > +      static_assert( std::input_iterator<Iter> ==
> > std::input_iterator<Wrapped> );
> > +      static_assert( std::forward_iterator<Iter> ==
> > std::forward_iterator<Wrapped> );
> > +      static_assert( std::bidirectional_iterator<Iter> ==
> > std::bidirectional_iterator<Wrapped> );
> > +      static_assert( std::random_access_iterator<Iter> ==
> > std::random_access_iterator<Wrapped> );
> > +    }
> > +}
> > +
> > +template<class Range, bool Const>
> > +void
> > +test02()
> > +{
> > +  if constexpr (Const)
> > +    {
> > +      static_assert( ranges::constant_range<Range> );
> > +      static_assert( std::same_as<ranges::const_iterator_t<Range>,
> > ranges::iterator_t<Range>> );
> > +      static_assert( std::same_as<ranges::const_sentinel_t<Range>,
> > ranges::sentinel_t<Range>> );
> > +      static_assert( std::same_as<ranges::range_const_reference_t<Range>,
> > +				 ranges::range_reference_t<Range>> );
> > +
> > +      static_assert(
> > std::same_as<decltype(ranges::cbegin(std::declval<Range&>())),
> > +
> > decltype(ranges::begin(std::declval<Range&>()))> );
> > +      static_assert(
> > std::same_as<decltype(ranges::cend(std::declval<Range&>())),
> > +
> > decltype(ranges::end(std::declval<Range&>()))> );
> > +    }
> > +  else
> > +    {
> > +      static_assert( !ranges::constant_range<Range> );
> > +      using Wrapped = std::basic_const_iterator<ranges::iterator_t<Range>>;
> > +
> > +      static_assert( std::same_as<ranges::const_iterator_t<Range>, Wrapped>
> > );
> > +      if constexpr (ranges::common_range<Range>)
> > +	static_assert( std::same_as<ranges::const_sentinel_t<Range>, Wrapped>
> > );
> > +      static_assert( std::same_as<ranges::range_const_reference_t<Range>,
> > +				 std::iter_reference_t<Wrapped>> );
> > +
> > +      static_assert( ranges::input_range<Range> ==
> > std::input_iterator<Wrapped> );
> > +      static_assert( ranges::forward_range<Range> ==
> > std::forward_iterator<Wrapped> );
> > +      static_assert( ranges::bidirectional_range<Range> ==
> > std::bidirectional_iterator<Wrapped> );
> > +      static_assert( ranges::random_access_range<Range> ==
> > std::random_access_iterator<Wrapped> );
> > +
> > +      if constexpr (ranges::constant_range<const Range&>)
> > +	{
> > +	  static_assert(
> > std::same_as<decltype(ranges::cbegin(std::declval<Range&>())),
> > +				     decltype(ranges::begin(std::declval<const
> > Range&>()))> );
> > +	  static_assert(
> > std::same_as<decltype(ranges::cend(std::declval<Range&>())),
> > +				     decltype(ranges::end(std::declval<const
> > Range&>()))> );
> > +	}
> > +      else
> > +	{
> > +	  static_assert(
> > std::same_as<decltype(ranges::cbegin(std::declval<Range&>())), Wrapped> );
> > +	  if constexpr (ranges::common_range<Range>)
> > +	    static_assert(
> > std::same_as<decltype(ranges::cend(std::declval<Range&>())), Wrapped> );
> > +	}
> > +    }
> > +}
> > +
> > +void
> > +test03()
> > +{
> > +  static_assert(
> > std::same_as<std::const_sentinel<std::unreachable_sentinel_t>,
> > +			     std::unreachable_sentinel_t> );
> > +}
> > +
> > +int
> > +main()
> > +{
> > +  test01<int*, false>();
> > +  test01<ranges::iterator_t<test_input_range<int>>, false>();
> > +  test01<ranges::iterator_t<test_forward_range<int>>, false>();
> > +  test01<ranges::iterator_t<test_bidirectional_range<int>>, false>();
> > +  test01<ranges::iterator_t<test_random_access_range<int>>, false>();
> > +  test01<std::array<int, 3>::iterator, false>();
> > +  test01<std::vector<bool>::iterator, false>();
> > +
> > +  test01<const int*, true>();
> > +  test01<ranges::iterator_t<test_input_range<const int>>, true>();
> > +  test01<ranges::iterator_t<test_forward_range<const int>>, true>();
> > +  test01<ranges::iterator_t<test_bidirectional_range<const int>>, true>();
> > +  test01<ranges::iterator_t<test_random_access_range<const int>>, true>();
> > +  test01<std::array<const int, 3>::iterator, true>();
> > +  test01<std::string_view::iterator, true>();
> > +  test01<std::vector<bool>::const_iterator, true>();
> > +
> > +  test02<int[42], false>();
> > +  test02<test_input_range<int>, false>();
> > +  test02<test_forward_range<int>, false>();
> > +  test02<test_bidirectional_range<int>, false>();
> > +  test02<test_random_access_range<int>, false>();
> > +  test02<std::array<int, 3>, false>();
> > +  test02<std::vector<bool>, false>();
> > +
> > +  test02<const int[42], true>();
> > +  test02<test_input_range<const int>, true>();
> > +  test02<test_forward_range<const int>, true>();
> > +  test02<test_bidirectional_range<const int>, true>();
> > +  test02<test_random_access_range<const int>, true>();
> > +  test02<std::array<const int, 3>, true>();
> > +  test02<const std::array<int, 3>, true>();
> > +  test02<std::string_view, true>();
> > +  test02<const std::vector<bool>, 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 <ranges>
> > +#include <algorithm>
> > +#include <span>
> > +#include <utility>
> > +#include <testsuite_hooks.h>
> > +#include <testsuite_iterators.h>
> > +
> > +#if __cpp_lib_ranges_as_const != 202207L
> > +# error "Feature-test macro __cpp_lib_ranges_as_const has wrong value in
> > <ranges>"
> > +#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<ty>);
> > +  static_assert(!ranges::constant_range<decltype(v.base())>);
> > +  static_assert(std::same_as<ranges::range_reference_t<ty>, const int&>);
> > +  static_assert(std::same_as<ranges::range_reference_t<decltype(v.base())>,
> > 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<ranges::empty_view<const int>> auto v1
> > +    = views::empty<int> | views::as_const;
> > +
> > +  int x[] = {1, 2, 3};
> > +  std::same_as<ranges::as_const_view<ranges::ref_view<int[3]>>> auto v2
> > +    = x | views::as_const;
> > +  std::same_as<ranges::ref_view<const int[3]>> auto v3
> > +    = std::as_const(x) | views::as_const;
> > +  std::same_as<ranges::ref_view<const int[3]>> auto v4
> > +    = std::as_const(x) | views::all | views::as_const;
> > +  std::same_as<std::span<const int>> auto v5
> > +    = std::span{x, x+3} | views::as_const;
> > +
> > +
> > std::same_as<ranges::as_const_view<ranges::chunk_view<ranges::ref_view<int[3]>>>>
> > 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 <sstream>
> > #include <string>
> > #include <string_view>
> > +#include <utility>
> > #include <vector>
> > #include <testsuite_hooks.h>
> > #include <testsuite_iterators.h>
> > @@ -120,8 +121,8 @@ test06()
> >   // Verify that _Sentinel<false> is implicitly convertible to
> > _Sentinel<true>.
> >   static_assert(!ranges::common_range<decltype(v)>);
> >   static_assert(!std::same_as<decltype(ranges::end(v)),
> > -			      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
> > <version>"
> > #endif
> > 
> > +#if __cpp_lib_ranges_as_const != 202207L
> > +# error "Feature-test macro __cpp_lib_ranges_as_const has wrong value in
> > <version>"
> > +#endif
> > +
> > #if __cpp_lib_ranges_enumerate != 202302L
> > # error "Feature-test macro __cpp_lib_ranges_enumerate has wrong value in
> > <version>"
> > #endif
> 
> 


  reply	other threads:[~2023-04-14 14:14 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-04-14  4:00 [PATCH 1/2] libstdc++: Move down definitions of ranges::cbegin/cend/cetc Patrick Palka
2023-04-14  4:00 ` [PATCH 2/2] libstdc++: Implement P2278R4 "cbegin should always return a constant iterator" Patrick Palka
2023-04-14  9:52   ` Ville Voutilainen
2023-04-14 10:14   ` Jonathan Wakely
2023-04-14 14:14     ` Patrick Palka [this message]
2023-04-14  9:49 ` [PATCH 1/2] libstdc++: Move down definitions of ranges::cbegin/cend/cetc Jonathan Wakely

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=6af61d9e-1afc-688a-0c4e-be74b5313506@idea \
    --to=ppalka@redhat.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=jwakely@redhat.com \
    --cc=libstdc++@gcc.gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).