public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH 1/2] libstdc++: Move down definitions of ranges::cbegin/cend/cetc
@ 2023-04-14  4:00 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:49 ` [PATCH 1/2] libstdc++: Move down definitions of ranges::cbegin/cend/cetc Jonathan Wakely
  0 siblings, 2 replies; 6+ messages in thread
From: Patrick Palka @ 2023-04-14  4:00 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Patrick Palka

This moves down the definitions of the const range access CPOs to after
the definition of input_range in preparation for implementing P2287R4
which redefines these CPOs in a way that indirectly uses input_range.

tested on x86_64-pc-linux-gnu, does this look OK for trunk?

libstdc++-v3/ChangeLog:

	* include/bits/ranges_base.h (__cust_access::__as_const)
	(__cust_access::_CBegin, __cust::cbegin)
	(__cust_access::_CEnd, __cust::cend)
	(__cust_access::_CRBegin, __cust::crbegin)
	(__cust_access::_CREnd, __cust::crend)
	(__cust_access::_CData, __cust::cdata): Move down definitions to
	shortly after the definition of input_range.
---
 libstdc++-v3/include/bits/ranges_base.h | 174 +++++++++++++-----------
 1 file changed, 91 insertions(+), 83 deletions(-)

diff --git a/libstdc++-v3/include/bits/ranges_base.h b/libstdc++-v3/include/bits/ranges_base.h
index 86952b34096..c89cb3e976a 100644
--- a/libstdc++-v3/include/bits/ranges_base.h
+++ b/libstdc++-v3/include/bits/ranges_base.h
@@ -177,45 +177,6 @@ namespace ranges
 	}
     };
 
-    // If _To is an lvalue-reference, return const _Tp&, otherwise const _Tp&&.
-    template<typename _To, typename _Tp>
-      constexpr decltype(auto)
-      __as_const(_Tp& __t) noexcept
-      {
-	static_assert(std::is_same_v<_To&, _Tp&>);
-
-	if constexpr (is_lvalue_reference_v<_To>)
-	  return const_cast<const _Tp&>(__t);
-	else
-	  return static_cast<const _Tp&&>(__t);
-      }
-
-    struct _CBegin
-    {
-      template<typename _Tp>
-	[[nodiscard]]
-	constexpr auto
-	operator()(_Tp&& __e) const
-	noexcept(noexcept(_Begin{}(__cust_access::__as_const<_Tp>(__e))))
-	requires requires { _Begin{}(__cust_access::__as_const<_Tp>(__e)); }
-	{
-	  return _Begin{}(__cust_access::__as_const<_Tp>(__e));
-	}
-    };
-
-    struct _CEnd final
-    {
-      template<typename _Tp>
-	[[nodiscard]]
-	constexpr auto
-	operator()(_Tp&& __e) const
-	noexcept(noexcept(_End{}(__cust_access::__as_const<_Tp>(__e))))
-	requires requires { _End{}(__cust_access::__as_const<_Tp>(__e)); }
-	{
-	  return _End{}(__cust_access::__as_const<_Tp>(__e));
-	}
-    };
-
     template<typename _Tp>
       concept __member_rbegin = requires(_Tp& __t)
 	{
@@ -337,32 +298,6 @@ namespace ranges
 	}
     };
 
-    struct _CRBegin
-    {
-      template<typename _Tp>
-	[[nodiscard]]
-	constexpr auto
-	operator()(_Tp&& __e) const
-	noexcept(noexcept(_RBegin{}(__cust_access::__as_const<_Tp>(__e))))
-	requires requires { _RBegin{}(__cust_access::__as_const<_Tp>(__e)); }
-	{
-	  return _RBegin{}(__cust_access::__as_const<_Tp>(__e));
-	}
-    };
-
-    struct _CREnd
-    {
-      template<typename _Tp>
-	[[nodiscard]]
-	constexpr auto
-	operator()(_Tp&& __e) const
-	noexcept(noexcept(_REnd{}(__cust_access::__as_const<_Tp>(__e))))
-	requires requires { _REnd{}(__cust_access::__as_const<_Tp>(__e)); }
-	{
-	  return _REnd{}(__cust_access::__as_const<_Tp>(__e));
-	}
-    };
-
     template<typename _Tp>
       concept __member_size = !disable_sized_range<remove_cvref_t<_Tp>>
 	&& requires(_Tp& __t)
@@ -547,36 +482,18 @@ namespace ranges
 	}
     };
 
-    struct _CData
-    {
-      template<typename _Tp>
-	[[nodiscard]]
-	constexpr auto
-	operator()(_Tp&& __e) const
-	noexcept(noexcept(_Data{}(__cust_access::__as_const<_Tp>(__e))))
-	requires requires { _Data{}(__cust_access::__as_const<_Tp>(__e)); }
-	{
-	  return _Data{}(__cust_access::__as_const<_Tp>(__e));
-	}
-    };
-
   } // namespace __cust_access
 
   inline namespace __cust
   {
     inline constexpr __cust_access::_Begin begin{};
     inline constexpr __cust_access::_End end{};
-    inline constexpr __cust_access::_CBegin cbegin{};
-    inline constexpr __cust_access::_CEnd cend{};
     inline constexpr __cust_access::_RBegin rbegin{};
     inline constexpr __cust_access::_REnd rend{};
-    inline constexpr __cust_access::_CRBegin crbegin{};
-    inline constexpr __cust_access::_CREnd crend{};
     inline constexpr __cust_access::_Size size{};
     inline constexpr __cust_access::_SSize ssize{};
     inline constexpr __cust_access::_Empty empty{};
     inline constexpr __cust_access::_Data data{};
-    inline constexpr __cust_access::_CData cdata{};
   }
 
   /// [range.range] The range concept.
@@ -690,6 +607,97 @@ namespace ranges
     concept common_range
       = range<_Tp> && same_as<iterator_t<_Tp>, sentinel_t<_Tp>>;
 
+  namespace __cust_access
+  {
+    // If _To is an lvalue-reference, return const _Tp&, otherwise const _Tp&&.
+    template<typename _To, typename _Tp>
+      constexpr decltype(auto)
+      __as_const(_Tp& __t) noexcept
+      {
+	static_assert(std::is_same_v<_To&, _Tp&>);
+
+	if constexpr (is_lvalue_reference_v<_To>)
+	  return const_cast<const _Tp&>(__t);
+	else
+	  return static_cast<const _Tp&&>(__t);
+      }
+
+    struct _CBegin
+    {
+      template<typename _Tp>
+	[[nodiscard]]
+	constexpr auto
+	operator()(_Tp&& __e) const
+	noexcept(noexcept(_Begin{}(__cust_access::__as_const<_Tp>(__e))))
+	requires requires { _Begin{}(__cust_access::__as_const<_Tp>(__e)); }
+	{
+	  return _Begin{}(__cust_access::__as_const<_Tp>(__e));
+	}
+    };
+
+    struct _CEnd final
+    {
+      template<typename _Tp>
+	[[nodiscard]]
+	constexpr auto
+	operator()(_Tp&& __e) const
+	noexcept(noexcept(_End{}(__cust_access::__as_const<_Tp>(__e))))
+	requires requires { _End{}(__cust_access::__as_const<_Tp>(__e)); }
+	{
+	  return _End{}(__cust_access::__as_const<_Tp>(__e));
+	}
+    };
+
+    struct _CRBegin
+    {
+      template<typename _Tp>
+	[[nodiscard]]
+	constexpr auto
+	operator()(_Tp&& __e) const
+	noexcept(noexcept(_RBegin{}(__cust_access::__as_const<_Tp>(__e))))
+	requires requires { _RBegin{}(__cust_access::__as_const<_Tp>(__e)); }
+	{
+	  return _RBegin{}(__cust_access::__as_const<_Tp>(__e));
+	}
+    };
+
+    struct _CREnd
+    {
+      template<typename _Tp>
+	[[nodiscard]]
+	constexpr auto
+	operator()(_Tp&& __e) const
+	noexcept(noexcept(_REnd{}(__cust_access::__as_const<_Tp>(__e))))
+	requires requires { _REnd{}(__cust_access::__as_const<_Tp>(__e)); }
+	{
+	  return _REnd{}(__cust_access::__as_const<_Tp>(__e));
+	}
+    };
+
+    struct _CData
+    {
+      template<typename _Tp>
+	[[nodiscard]]
+	constexpr auto
+	operator()(_Tp&& __e) const
+	noexcept(noexcept(_Data{}(__cust_access::__as_const<_Tp>(__e))))
+	requires requires { _Data{}(__cust_access::__as_const<_Tp>(__e)); }
+	{
+	  return _Data{}(__cust_access::__as_const<_Tp>(__e));
+	}
+    };
+
+  } // namespace __cust_access
+
+  inline namespace __cust
+  {
+    inline constexpr __cust_access::_CBegin cbegin{};
+    inline constexpr __cust_access::_CEnd cend{};
+    inline constexpr __cust_access::_CRBegin crbegin{};
+    inline constexpr __cust_access::_CREnd crend{};
+    inline constexpr __cust_access::_CData cdata{};
+  }
+
   namespace __detail
   {
     template<typename _Tp>
-- 
2.40.0.335.g9857273be0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 2/2] libstdc++: Implement P2278R4 "cbegin should always return a constant iterator"
  2023-04-14  4:00 [PATCH 1/2] libstdc++: Move down definitions of ranges::cbegin/cend/cetc Patrick Palka
@ 2023-04-14  4:00 ` Patrick Palka
  2023-04-14  9:52   ` Ville Voutilainen
  2023-04-14 10:14   ` Jonathan Wakely
  2023-04-14  9:49 ` [PATCH 1/2] libstdc++: Move down definitions of ranges::cbegin/cend/cetc Jonathan Wakely
  1 sibling, 2 replies; 6+ messages in thread
From: Patrick Palka @ 2023-04-14  4:00 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Patrick Palka

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))); }
+	{
+	  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
+	__as_const_pointer(const _Tp* __p) noexcept
+	{ return __p; }
+
+      template<__maybe_borrowed_range _Tp>
+	constexpr auto
+	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>
+  {
+    _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
-- 
2.40.0.335.g9857273be0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 1/2] libstdc++: Move down definitions of ranges::cbegin/cend/cetc
  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:49 ` Jonathan Wakely
  1 sibling, 0 replies; 6+ messages in thread
From: Jonathan Wakely @ 2023-04-14  9:49 UTC (permalink / raw)
  To: Patrick Palka; +Cc: gcc-patches, libstdc++

On Fri, 14 Apr 2023 at 05:02, Patrick Palka via Libstdc++
<libstdc++@gcc.gnu.org> wrote:
>
> This moves down the definitions of the const range access CPOs to after
> the definition of input_range in preparation for implementing P2287R4
> which redefines these CPOs in a way that indirectly uses input_range.
>
> tested on x86_64-pc-linux-gnu, does this look OK for trunk?

OK


>
> libstdc++-v3/ChangeLog:
>
>         * include/bits/ranges_base.h (__cust_access::__as_const)
>         (__cust_access::_CBegin, __cust::cbegin)
>         (__cust_access::_CEnd, __cust::cend)
>         (__cust_access::_CRBegin, __cust::crbegin)
>         (__cust_access::_CREnd, __cust::crend)
>         (__cust_access::_CData, __cust::cdata): Move down definitions to
>         shortly after the definition of input_range.
> ---
>  libstdc++-v3/include/bits/ranges_base.h | 174 +++++++++++++-----------
>  1 file changed, 91 insertions(+), 83 deletions(-)
>
> diff --git a/libstdc++-v3/include/bits/ranges_base.h b/libstdc++-v3/include/bits/ranges_base.h
> index 86952b34096..c89cb3e976a 100644
> --- a/libstdc++-v3/include/bits/ranges_base.h
> +++ b/libstdc++-v3/include/bits/ranges_base.h
> @@ -177,45 +177,6 @@ namespace ranges
>         }
>      };
>
> -    // If _To is an lvalue-reference, return const _Tp&, otherwise const _Tp&&.
> -    template<typename _To, typename _Tp>
> -      constexpr decltype(auto)
> -      __as_const(_Tp& __t) noexcept
> -      {
> -       static_assert(std::is_same_v<_To&, _Tp&>);
> -
> -       if constexpr (is_lvalue_reference_v<_To>)
> -         return const_cast<const _Tp&>(__t);
> -       else
> -         return static_cast<const _Tp&&>(__t);
> -      }
> -
> -    struct _CBegin
> -    {
> -      template<typename _Tp>
> -       [[nodiscard]]
> -       constexpr auto
> -       operator()(_Tp&& __e) const
> -       noexcept(noexcept(_Begin{}(__cust_access::__as_const<_Tp>(__e))))
> -       requires requires { _Begin{}(__cust_access::__as_const<_Tp>(__e)); }
> -       {
> -         return _Begin{}(__cust_access::__as_const<_Tp>(__e));
> -       }
> -    };
> -
> -    struct _CEnd final
> -    {
> -      template<typename _Tp>
> -       [[nodiscard]]
> -       constexpr auto
> -       operator()(_Tp&& __e) const
> -       noexcept(noexcept(_End{}(__cust_access::__as_const<_Tp>(__e))))
> -       requires requires { _End{}(__cust_access::__as_const<_Tp>(__e)); }
> -       {
> -         return _End{}(__cust_access::__as_const<_Tp>(__e));
> -       }
> -    };
> -
>      template<typename _Tp>
>        concept __member_rbegin = requires(_Tp& __t)
>         {
> @@ -337,32 +298,6 @@ namespace ranges
>         }
>      };
>
> -    struct _CRBegin
> -    {
> -      template<typename _Tp>
> -       [[nodiscard]]
> -       constexpr auto
> -       operator()(_Tp&& __e) const
> -       noexcept(noexcept(_RBegin{}(__cust_access::__as_const<_Tp>(__e))))
> -       requires requires { _RBegin{}(__cust_access::__as_const<_Tp>(__e)); }
> -       {
> -         return _RBegin{}(__cust_access::__as_const<_Tp>(__e));
> -       }
> -    };
> -
> -    struct _CREnd
> -    {
> -      template<typename _Tp>
> -       [[nodiscard]]
> -       constexpr auto
> -       operator()(_Tp&& __e) const
> -       noexcept(noexcept(_REnd{}(__cust_access::__as_const<_Tp>(__e))))
> -       requires requires { _REnd{}(__cust_access::__as_const<_Tp>(__e)); }
> -       {
> -         return _REnd{}(__cust_access::__as_const<_Tp>(__e));
> -       }
> -    };
> -
>      template<typename _Tp>
>        concept __member_size = !disable_sized_range<remove_cvref_t<_Tp>>
>         && requires(_Tp& __t)
> @@ -547,36 +482,18 @@ namespace ranges
>         }
>      };
>
> -    struct _CData
> -    {
> -      template<typename _Tp>
> -       [[nodiscard]]
> -       constexpr auto
> -       operator()(_Tp&& __e) const
> -       noexcept(noexcept(_Data{}(__cust_access::__as_const<_Tp>(__e))))
> -       requires requires { _Data{}(__cust_access::__as_const<_Tp>(__e)); }
> -       {
> -         return _Data{}(__cust_access::__as_const<_Tp>(__e));
> -       }
> -    };
> -
>    } // namespace __cust_access
>
>    inline namespace __cust
>    {
>      inline constexpr __cust_access::_Begin begin{};
>      inline constexpr __cust_access::_End end{};
> -    inline constexpr __cust_access::_CBegin cbegin{};
> -    inline constexpr __cust_access::_CEnd cend{};
>      inline constexpr __cust_access::_RBegin rbegin{};
>      inline constexpr __cust_access::_REnd rend{};
> -    inline constexpr __cust_access::_CRBegin crbegin{};
> -    inline constexpr __cust_access::_CREnd crend{};
>      inline constexpr __cust_access::_Size size{};
>      inline constexpr __cust_access::_SSize ssize{};
>      inline constexpr __cust_access::_Empty empty{};
>      inline constexpr __cust_access::_Data data{};
> -    inline constexpr __cust_access::_CData cdata{};
>    }
>
>    /// [range.range] The range concept.
> @@ -690,6 +607,97 @@ namespace ranges
>      concept common_range
>        = range<_Tp> && same_as<iterator_t<_Tp>, sentinel_t<_Tp>>;
>
> +  namespace __cust_access
> +  {
> +    // If _To is an lvalue-reference, return const _Tp&, otherwise const _Tp&&.
> +    template<typename _To, typename _Tp>
> +      constexpr decltype(auto)
> +      __as_const(_Tp& __t) noexcept
> +      {
> +       static_assert(std::is_same_v<_To&, _Tp&>);
> +
> +       if constexpr (is_lvalue_reference_v<_To>)
> +         return const_cast<const _Tp&>(__t);
> +       else
> +         return static_cast<const _Tp&&>(__t);
> +      }
> +
> +    struct _CBegin
> +    {
> +      template<typename _Tp>
> +       [[nodiscard]]
> +       constexpr auto
> +       operator()(_Tp&& __e) const
> +       noexcept(noexcept(_Begin{}(__cust_access::__as_const<_Tp>(__e))))
> +       requires requires { _Begin{}(__cust_access::__as_const<_Tp>(__e)); }
> +       {
> +         return _Begin{}(__cust_access::__as_const<_Tp>(__e));
> +       }
> +    };
> +
> +    struct _CEnd final
> +    {
> +      template<typename _Tp>
> +       [[nodiscard]]
> +       constexpr auto
> +       operator()(_Tp&& __e) const
> +       noexcept(noexcept(_End{}(__cust_access::__as_const<_Tp>(__e))))
> +       requires requires { _End{}(__cust_access::__as_const<_Tp>(__e)); }
> +       {
> +         return _End{}(__cust_access::__as_const<_Tp>(__e));
> +       }
> +    };
> +
> +    struct _CRBegin
> +    {
> +      template<typename _Tp>
> +       [[nodiscard]]
> +       constexpr auto
> +       operator()(_Tp&& __e) const
> +       noexcept(noexcept(_RBegin{}(__cust_access::__as_const<_Tp>(__e))))
> +       requires requires { _RBegin{}(__cust_access::__as_const<_Tp>(__e)); }
> +       {
> +         return _RBegin{}(__cust_access::__as_const<_Tp>(__e));
> +       }
> +    };
> +
> +    struct _CREnd
> +    {
> +      template<typename _Tp>
> +       [[nodiscard]]
> +       constexpr auto
> +       operator()(_Tp&& __e) const
> +       noexcept(noexcept(_REnd{}(__cust_access::__as_const<_Tp>(__e))))
> +       requires requires { _REnd{}(__cust_access::__as_const<_Tp>(__e)); }
> +       {
> +         return _REnd{}(__cust_access::__as_const<_Tp>(__e));
> +       }
> +    };
> +
> +    struct _CData
> +    {
> +      template<typename _Tp>
> +       [[nodiscard]]
> +       constexpr auto
> +       operator()(_Tp&& __e) const
> +       noexcept(noexcept(_Data{}(__cust_access::__as_const<_Tp>(__e))))
> +       requires requires { _Data{}(__cust_access::__as_const<_Tp>(__e)); }
> +       {
> +         return _Data{}(__cust_access::__as_const<_Tp>(__e));
> +       }
> +    };
> +
> +  } // namespace __cust_access
> +
> +  inline namespace __cust
> +  {
> +    inline constexpr __cust_access::_CBegin cbegin{};
> +    inline constexpr __cust_access::_CEnd cend{};
> +    inline constexpr __cust_access::_CRBegin crbegin{};
> +    inline constexpr __cust_access::_CREnd crend{};
> +    inline constexpr __cust_access::_CData cdata{};
> +  }
> +
>    namespace __detail
>    {
>      template<typename _Tp>
> --
> 2.40.0.335.g9857273be0
>


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 2/2] libstdc++: Implement P2278R4 "cbegin should always return a constant iterator"
  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
  1 sibling, 0 replies; 6+ messages in thread
From: Ville Voutilainen @ 2023-04-14  9:52 UTC (permalink / raw)
  To: Patrick Palka; +Cc: gcc-patches, libstdc++

On Fri, 14 Apr 2023 at 07:03, Patrick Palka via Libstdc++
<libstdc++@gcc.gnu.org> 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?

Hooray! THANK YOU!

No comments on the patch itself, Jonathan will review it. :)

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 2/2] libstdc++: Implement P2278R4 "cbegin should always return a constant iterator"
  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
  1 sibling, 1 reply; 6+ messages in thread
From: Jonathan Wakely @ 2023-04-14 10:14 UTC (permalink / raw)
  To: Patrick Palka; +Cc: gcc-patches, libstdc++

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 :-)

>+	{
>+	  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?

>+	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.

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<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


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 2/2] libstdc++: Implement P2278R4 "cbegin should always return a constant iterator"
  2023-04-14 10:14   ` Jonathan Wakely
@ 2023-04-14 14:14     ` Patrick Palka
  0 siblings, 0 replies; 6+ messages in thread
From: Patrick Palka @ 2023-04-14 14:14 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: Patrick Palka, gcc-patches, libstdc++

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
> 
> 


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2023-04-14 14:14 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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
2023-04-14  9:49 ` [PATCH 1/2] libstdc++: Move down definitions of ranges::cbegin/cend/cetc Jonathan Wakely

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).