[libcxx-commits] [libcxx] [libcxx] P2278R4: cbegin and friends (PR #99915)

nicole mazzuca via libcxx-commits libcxx-commits at lists.llvm.org
Mon Jul 22 11:49:40 PDT 2024


https://github.com/strega-nil created https://github.com/llvm/llvm-project/pull/99915

Implements `basic_const_iterator` and the const accessors.

Does not implement `const_view`.

>From 57bb74c215a2a9c7274e5813c649934ea70c280b Mon Sep 17 00:00:00 2001
From: Nicole Mazzuca <nicole at strega-nil.co>
Date: Mon, 22 Jul 2024 15:39:27 +0200
Subject: [PATCH] [libcxx] P2278R4: cbegin and friends

Implements `basic_const_iterator` and the const accessors.

Does not implement `const_view`
---
 libcxx/docs/Status/Cxx23Papers.csv         |   2 +-
 libcxx/include/CMakeLists.txt              |   2 +
 libcxx/include/__iterator/const_iterator.h | 328 +++++++++++++++++++++
 libcxx/include/__ranges/access.h           |  55 ----
 libcxx/include/__ranges/concepts.h         |  14 +
 libcxx/include/__ranges/const_access.h     | 202 +++++++++++++
 libcxx/include/__ranges/rbegin.h           |  28 --
 libcxx/include/__ranges/rend.h             |  27 --
 libcxx/include/iterator                    |  29 ++
 libcxx/include/ranges                      |   8 +
 libcxx/include/span                        |  25 ++
 11 files changed, 609 insertions(+), 111 deletions(-)
 create mode 100644 libcxx/include/__iterator/const_iterator.h
 create mode 100644 libcxx/include/__ranges/const_access.h

diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index f8970320ce1fa..2d294e8501e5b 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -61,7 +61,7 @@
 "`P1899R3 <https://wg21.link/P1899R3>`__","LWG","``stride_view``","July 2022","","","|ranges|"
 "`P2093R14 <https://wg21.link/P2093R14>`__","LWG","Formatted output","July 2022","|Complete|","18.0","|format|"
 "`P2165R4 <https://wg21.link/P2165R4>`__","LWG","Compatibility between ``tuple``, ``pair`` and ``tuple-like`` objects","July 2022","",""
-"`P2278R4 <https://wg21.link/P2278R4>`__","LWG","``cbegin`` should always return a constant iterator","July 2022","","","|ranges|"
+"`P2278R4 <https://wg21.link/P2278R4>`__","LWG","``cbegin`` should always return a constant iterator","July 2022","In Progress","","|ranges|"
 "`P2286R8 <https://wg21.link/P2286R8>`__","LWG","Formatting Ranges","July 2022","|Complete|","16.0","|format| |ranges|"
 "`P2291R3 <https://wg21.link/P2291R3>`__","LWG","Add Constexpr Modifiers to Functions ``to_chars`` and ``from_chars`` for Integral Types in ``<charconv>`` Header","July 2022","|Complete|","16.0"
 "`P2302R4 <https://wg21.link/P2302R4>`__","LWG","``std::ranges::contains``","July 2022","|Complete|","19.0","|ranges|"
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 32579272858a8..577b7975f3eb2 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -444,6 +444,7 @@ set(files
   __iterator/bounded_iter.h
   __iterator/common_iterator.h
   __iterator/concepts.h
+  __iterator/const_iterator.h
   __iterator/counted_iterator.h
   __iterator/cpp17_iterator_concepts.h
   __iterator/data.h
@@ -639,6 +640,7 @@ set(files
   __ranges/chunk_by_view.h
   __ranges/common_view.h
   __ranges/concepts.h
+  __ranges/const_access.h
   __ranges/container_compatible_range.h
   __ranges/counted.h
   __ranges/dangling.h
diff --git a/libcxx/include/__iterator/const_iterator.h b/libcxx/include/__iterator/const_iterator.h
new file mode 100644
index 0000000000000..7549a98fe3ecc
--- /dev/null
+++ b/libcxx/include/__iterator/const_iterator.h
@@ -0,0 +1,328 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___ITERATOR_CONST_ITERATOR_H
+#define _LIBCPP___ITERATOR_CONST_ITERATOR_H
+
+#include <__compare/three_way_comparable.h>
+#include <__concepts/common_with.h>
+#include <__concepts/different_from.h>
+#include <__iterator/concepts.h>
+#include <__iterator/iterator_traits.h>
+#include <__type_traits/common_reference.h>
+#include <__type_traits/common_type.h>
+#include <__type_traits/integral_constant.h>
+#include <__type_traits/is_specialization.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+template <indirectly_readable _Iter>
+using iter_const_reference_t = common_reference_t<const iter_value_t<_Iter>&&, iter_reference_t<_Iter>>;
+
+template <class _Iter>
+concept __constant_iterator = input_iterator<_Iter> && same_as<iter_const_reference_t<_Iter>, iter_reference_t<_Iter>>;
+
+template <input_iterator _Iter>
+class basic_const_iterator;
+
+template <input_iterator _Iter>
+using const_iterator = conditional_t<__constant_iterator<_Iter>, _Iter, basic_const_iterator<_Iter>>;
+template <semiregular _Sent>
+using const_sentinel = conditional_t<input_iterator<_Sent>, const_iterator<_Sent>, _Sent>;
+
+template <class _Iter>
+concept __not_a_const_iterator = !__is_specialization_v<_Iter, basic_const_iterator>;
+
+template <indirectly_readable _Iter>
+using __iter_const_rvalue_reference_t = common_reference_t<const iter_value_t<_Iter>&&, iter_rvalue_reference_t<_Iter>>;
+
+template <class _Iter>
+struct __basic_const_iterator_concept {
+  // clang-format off
+  using iterator_concept =
+    conditional_t<contiguous_iterator<_Iter>,
+      contiguous_iterator_tag,
+    conditional_t<random_access_iterator<_Iter>,
+      random_access_iterator_tag,
+    conditional_t<bidirectional_iterator<_Iter>,
+      bidirectional_iterator_tag,
+    conditional_t<forward_iterator<_Iter>,
+      forward_iterator_tag,
+    // else
+      input_iterator_tag>>>>;
+  // clang-format on
+};
+
+template <class _Iter>
+struct __basic_const_iterator_category : __basic_const_iterator_concept<_Iter> {};
+template <forward_iterator _Iter>
+struct __basic_const_iterator_category<_Iter> : __basic_const_iterator_concept<_Iter> {
+  using iterator_category = __basic_const_iterator_concept<_Iter>::iterator_concept;
+};
+
+template <input_iterator _Iter>
+class _LIBCPP_TEMPLATE_VIS basic_const_iterator : __basic_const_iterator_category<_Iter> {
+  _Iter __current = _Iter();
+
+  using __reference        = iter_const_reference_t<_Iter>;
+  using __rvalue_reference = __iter_const_rvalue_reference_t<_Iter>;
+
+public:
+  using value_type      = iter_value_t<_Iter>;
+  using difference_type = iter_difference_t<_Iter>;
+
+  _LIBCPP_HIDE_FROM_ABI basic_const_iterator()
+    requires default_initializable<_Iter>
+  = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator(_Iter __cur) : __current(std::move(__cur)) {}
+  template <convertible_to<_Iter> _Type>
+  _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator(basic_const_iterator<_Type> __cur)
+      : __current(std::move(__cur.__current)) {}
+  template <__different_from<basic_const_iterator> _Type>
+    requires convertible_to<_Type, _Iter>
+  _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator(_Type&& __cur) : __current(std::forward<_Type>(__cur)) {}
+
+  _LIBCPP_HIDE_FROM_ABI constexpr const _Iter& base() const& noexcept { return __current; }
+  _LIBCPP_HIDE_FROM_ABI constexpr _Iter base() && { return std::move(__current); }
+
+  constexpr __reference operator*() const { return static_cast<__reference>(*__current); }
+  _LIBCPP_HIDE_FROM_ABI constexpr const auto* operator->() const
+    requires is_lvalue_reference_v<iter_reference_t<_Iter>> &&
+             same_as<remove_cvref_t<iter_reference_t<_Iter>>, value_type>
+  {
+    if constexpr (contiguous_iterator<_Iter>) {
+      return std::to_address(__current);
+    } else {
+      return std::addressof(*__current);
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator& operator++() {
+    ++__current;
+    return *this;
+  }
+  constexpr void operator++(int) { ++__current; }
+  _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator operator++(int)
+    requires forward_iterator<_Iter>
+  {
+    auto __tmp = *this;
+    ++__current;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator& operator--()
+    requires bidirectional_iterator<_Iter>
+  {
+    --__current;
+    return *this;
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator operator--(int)
+    requires bidirectional_iterator<_Iter>
+  {
+    auto __tmp = *this;
+    --__current;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator& operator+=(difference_type __n)
+    requires random_access_iterator<_Iter>
+  {
+    __current += __n;
+    return *this;
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator& operator-=(difference_type __n)
+    requires random_access_iterator<_Iter>
+  {
+    __current -= __n;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __reference operator[](difference_type __n) const
+    requires random_access_iterator<_Iter>
+  {
+    return static_cast<__reference>(__current[__n]);
+  }
+
+  template <sentinel_for<_Iter> _Sent>
+  _LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const _Sent& __sent) const {
+    return __current == __sent;
+  }
+
+  template <__not_a_const_iterator _ConstIt>
+    requires __constant_iterator<_ConstIt> && convertible_to<_Iter const&, _ConstIt>
+  _LIBCPP_HIDE_FROM_ABI constexpr operator _ConstIt() const& {
+    return __current;
+  }
+  template <__not_a_const_iterator _ConstIt>
+    requires __constant_iterator<_ConstIt> && convertible_to<_Iter, _ConstIt>
+  _LIBCPP_HIDE_FROM_ABI constexpr operator _ConstIt() && {
+    return std::move(__current);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const basic_const_iterator& __rhs) const
+    requires random_access_iterator<_Iter>
+  {
+    return __current < __rhs.__current;
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const basic_const_iterator& __rhs) const
+    requires random_access_iterator<_Iter>
+  {
+    return __current > __rhs.__current;
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const basic_const_iterator& __rhs) const
+    requires random_access_iterator<_Iter>
+  {
+    return __current <= __rhs.__current;
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const basic_const_iterator& __rhs) const
+    requires random_access_iterator<_Iter>
+  {
+    return __current >= __rhs.__current;
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr auto operator<=>(const basic_const_iterator& __rhs) const
+    requires random_access_iterator<_Iter> && three_way_comparable<_Iter>
+  {
+    return __current <=> __rhs.__current;
+  }
+
+  template <__different_from<basic_const_iterator> _Iter2>
+  _LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const _Iter2& __rhs) const
+    requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Iter2>
+  {
+    return __current < __rhs.__current;
+  }
+  template <__different_from<basic_const_iterator> _Iter2>
+  _LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const _Iter2& __rhs) const
+    requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Iter2>
+  {
+    return __current > __rhs.__current;
+  }
+  template <__different_from<basic_const_iterator> _Iter2>
+  _LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const _Iter2& __rhs) const
+    requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Iter2>
+  {
+    return __current <= __rhs.__current;
+  }
+  template <__different_from<basic_const_iterator> _Iter2>
+  _LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const _Iter2& __rhs) const
+    requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Iter2>
+  {
+    return __current >= __rhs.__current;
+  }
+  template <__different_from<basic_const_iterator> _Iter2>
+  _LIBCPP_HIDE_FROM_ABI constexpr auto operator<=>(const _Iter2& __rhs) const
+    requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Iter2> &&
+             three_way_comparable_with<_Iter, _Iter2>
+  {
+    return __current <=> __rhs.__current;
+  }
+
+  template <__not_a_const_iterator _Iter2>
+  friend _LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const _Iter2& __lhs, const basic_const_iterator& __rhs)
+    requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Iter2>
+  {
+    return __lhs < __rhs.__current;
+  }
+  template <__not_a_const_iterator _Iter2>
+  friend _LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const _Iter2& __lhs, const basic_const_iterator& __rhs)
+    requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Iter2>
+  {
+    return __lhs > __rhs.__current;
+  }
+  template <__not_a_const_iterator _Iter2>
+  friend _LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const _Iter2& __lhs, const basic_const_iterator& __rhs)
+    requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Iter2>
+  {
+    return __lhs <= __rhs.__current;
+  }
+  template <__not_a_const_iterator _Iter2>
+  friend _LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const _Iter2& __lhs, const basic_const_iterator& __rhs)
+    requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Iter2>
+  {
+    return __lhs >= __rhs.__current;
+  }
+
+  friend _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator
+  operator+(const basic_const_iterator& __it, difference_type __n)
+    requires random_access_iterator<_Iter>
+  {
+    return basic_const_iterator(__it.__current + __n);
+  }
+  friend _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator
+  operator+(difference_type __n, const basic_const_iterator& __it)
+    requires random_access_iterator<_Iter>
+  {
+    return basic_const_iterator(__it.__current + __n);
+  }
+
+  friend _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator
+  operator-(const basic_const_iterator& __it, difference_type __n)
+    requires random_access_iterator<_Iter>
+  {
+    return basic_const_iterator(__it.__current - __n);
+  }
+  template <sized_sentinel_for<_Iter> _Sent>
+  _LIBCPP_HIDE_FROM_ABI constexpr difference_type operator-(const _Sent& __rhs) const {
+    return __current - __rhs;
+  }
+  template <__not_a_const_iterator _Sent>
+    requires sized_sentinel_for<_Sent, _Iter>
+  friend _LIBCPP_HIDE_FROM_ABI constexpr difference_type
+  operator-(const _Sent& __lhs, const basic_const_iterator& __rhs) {
+    return __lhs - __rhs;
+  }
+
+  friend _LIBCPP_HIDE_FROM_ABI constexpr __rvalue_reference iter_move(const basic_const_iterator& __it) noexcept(
+      noexcept(static_cast<__rvalue_reference>(ranges::iter_move(__it.current_)))) {
+    return static_cast<__rvalue_reference>(ranges::iter_move(__it.current_));
+  }
+};
+
+template <class _Type1, common_with<_Type1> _Type2>
+  requires input_iterator<common_type_t<_Type1, _Type2>>
+struct common_type<basic_const_iterator<_Type1>, _Type2> {
+  using type = basic_const_iterator<common_type_t<_Type1, _Type2>>;
+};
+template <class _Type1, common_with<_Type1> _Type2>
+  requires input_iterator<common_type_t<_Type1, _Type2>>
+struct common_type<_Type2, basic_const_iterator<_Type1>> {
+  using type = basic_const_iterator<common_type_t<_Type1, _Type2>>;
+};
+template <class _Type1, common_with<_Type1> _Type2>
+  requires input_iterator<common_type_t<_Type1, _Type2>>
+struct common_type<basic_const_iterator<_Type1>, basic_const_iterator<_Type2>> {
+  using type = basic_const_iterator<common_type_t<_Type1, _Type2>>;
+};
+
+template <input_iterator _Iter>
+_LIBCPP_HIDE_FROM_ABI constexpr const_iterator<_Iter> make_const_iterator(_Iter __it) {
+  return __it;
+}
+template <semiregular _Sent>
+_LIBCPP_HIDE_FROM_ABI constexpr const_sentinel<_Sent> make_const_sentinel(_Sent __sent) {
+  return __sent;
+}
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___ITERATOR_CONST_ITERATOR_H
diff --git a/libcxx/include/__ranges/access.h b/libcxx/include/__ranges/access.h
index c0a40c5e10178..7955b3b2166dc 100644
--- a/libcxx/include/__ranges/access.h
+++ b/libcxx/include/__ranges/access.h
@@ -148,61 +148,6 @@ inline constexpr auto end = __end::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
-// [range.access.cbegin]
-
-namespace ranges {
-namespace __cbegin {
-struct __fn {
-  template <class _Tp>
-    requires is_lvalue_reference_v<_Tp&&>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
-      noexcept(noexcept(ranges::begin(static_cast<const remove_reference_t<_Tp>&>(__t))))
-          -> decltype(ranges::begin(static_cast<const remove_reference_t<_Tp>&>(__t))) {
-    return ranges::begin(static_cast<const remove_reference_t<_Tp>&>(__t));
-  }
-
-  template <class _Tp>
-    requires is_rvalue_reference_v<_Tp&&>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
-      noexcept(noexcept(ranges::begin(static_cast<const _Tp&&>(__t))))
-          -> decltype(ranges::begin(static_cast<const _Tp&&>(__t))) {
-    return ranges::begin(static_cast<const _Tp&&>(__t));
-  }
-};
-} // namespace __cbegin
-
-inline namespace __cpo {
-inline constexpr auto cbegin = __cbegin::__fn{};
-} // namespace __cpo
-} // namespace ranges
-
-// [range.access.cend]
-
-namespace ranges {
-namespace __cend {
-struct __fn {
-  template <class _Tp>
-    requires is_lvalue_reference_v<_Tp&&>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
-      noexcept(noexcept(ranges::end(static_cast<const remove_reference_t<_Tp>&>(__t))))
-          -> decltype(ranges::end(static_cast<const remove_reference_t<_Tp>&>(__t))) {
-    return ranges::end(static_cast<const remove_reference_t<_Tp>&>(__t));
-  }
-
-  template <class _Tp>
-    requires is_rvalue_reference_v<_Tp&&>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const noexcept(
-      noexcept(ranges::end(static_cast<const _Tp&&>(__t)))) -> decltype(ranges::end(static_cast<const _Tp&&>(__t))) {
-    return ranges::end(static_cast<const _Tp&&>(__t));
-  }
-};
-} // namespace __cend
-
-inline namespace __cpo {
-inline constexpr auto cend = __cend::__fn{};
-} // namespace __cpo
-} // namespace ranges
-
 #endif // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h
index 674a3f359ff99..7a638817ac7ed 100644
--- a/libcxx/include/__ranges/concepts.h
+++ b/libcxx/include/__ranges/concepts.h
@@ -15,6 +15,7 @@
 #include <__concepts/same_as.h>
 #include <__config>
 #include <__iterator/concepts.h>
+#include <__iterator/const_iterator.h>
 #include <__iterator/incrementable_traits.h>
 #include <__iterator/iter_move.h>
 #include <__iterator/iterator_traits.h>
@@ -61,6 +62,11 @@ concept borrowed_range =
 template <range _Rp>
 using sentinel_t = decltype(ranges::end(std::declval<_Rp&>()));
 
+#if _LIBCPP_STD_VER >= 23
+template <range _Rp>
+using const_iterator_t = const_iterator<iterator_t<_Rp>>;
+#endif // _LIBCPP_STD_VER >= 23
+
 template <range _Rp>
 using range_difference_t = iter_difference_t<iterator_t<_Rp>>;
 
@@ -70,6 +76,9 @@ using range_value_t = iter_value_t<iterator_t<_Rp>>;
 template <range _Rp>
 using range_reference_t = iter_reference_t<iterator_t<_Rp>>;
 
+template<range _Rp>
+using range_const_reference_t = iter_const_reference_t<iterator_t<_Rp>>;
+
 template <range _Rp>
 using range_rvalue_reference_t = iter_rvalue_reference_t<iterator_t<_Rp>>;
 
@@ -133,6 +142,11 @@ concept viewable_range =
       (is_lvalue_reference_v<_Tp> ||
        (movable<remove_reference_t<_Tp>> && !__is_std_initializer_list<remove_cvref_t<_Tp>>))));
 
+#if _LIBCPP_STD_VER >= 23
+template <class _Tp>
+concept constant_range = input_range<_Tp> && __constant_iterator<iterator_t<_Tp>>;
+#endif // _LIBCPP_STD_VER >= 23
+
 } // namespace ranges
 
 #endif // _LIBCPP_STD_VER >= 20
diff --git a/libcxx/include/__ranges/const_access.h b/libcxx/include/__ranges/const_access.h
new file mode 100644
index 0000000000000..9f38fc1d717c5
--- /dev/null
+++ b/libcxx/include/__ranges/const_access.h
@@ -0,0 +1,202 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___RANGES_CONST_ACCESS_H
+#define _LIBCPP___RANGES_CONST_ACCESS_H
+
+#include <__iterator/const_iterator.h>
+#include <__ranges/access.h>
+#include <__ranges/concepts.h>
+#include <__ranges/enable_borrowed_range.h>
+#include <__ranges/rbegin.h>
+#include <__ranges/rend.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/remove_reference.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20
+
+// [range.const]
+#  if _LIBCPP_STD_VER >= 23
+namespace ranges {
+template <input_range _Rp>
+constexpr auto& __possibly_const_range(_Rp& __rng) {
+  if constexpr (constant_range<const _Rp> && !constant_range<_Rp>) {
+    return const_cast<const _Rp&>(__rng);
+  } else {
+    return __rng;
+  }
+}
+} // namespace ranges
+#  endif // _LIBCPP_STD_VER >= 23
+
+// [range.access.cbegin]
+namespace ranges {
+
+template <class _Type>
+concept __const_accessible_range = (!is_rvalue_reference_v<_Type&&> || enable_borrowed_range<remove_cv_t<_Type>>);
+
+namespace __cbegin {
+struct __fn {
+#  if _LIBCPP_STD_VER >= 23
+  template <class _Rng>
+  using _UType = decltype(ranges::begin(ranges::__possibly_const_range(std::declval<_Rng>())));
+
+  template <__const_accessible_range _Rng>
+  auto operator()(_Type&& __rng) noexcept(noexcept(const_iterator<_UType<_Rng>>(
+      ranges::begin(ranges::__possibly_const_range(std::declval<_Rng>()))))) -> const_iterator<_UType<_Rng>> {
+    return const_iterator<_UType<_Rng>>(ranges::begin(ranges::__possibly_const_range(__rng)));
+  }
+#  else  // ^^^ _LIBCPP_STD_VER >= 23 / _LIBCPP_STD_VER < 23 vvv
+  template <class _Tp>
+    requires is_lvalue_reference_v<_Tp&&>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::begin(static_cast<const remove_reference_t<_Tp>&>(__t))))
+          -> decltype(ranges::begin(static_cast<const remove_reference_t<_Tp>&>(__t))) {
+    return ranges::begin(static_cast<const remove_reference_t<_Tp>&>(__t));
+  }
+
+  template <class _Tp>
+    requires is_rvalue_reference_v<_Tp&&>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::begin(static_cast<const _Tp&&>(__t))))
+          -> decltype(ranges::begin(static_cast<const _Tp&&>(__t))) {
+    return ranges::begin(static_cast<const _Tp&&>(__t));
+  }
+#  endif // ^^^ _LIBCPP_STD_VER < 23
+};
+} // namespace __cbegin
+
+inline namespace __cpo {
+inline constexpr auto cbegin = __cbegin::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+// [range.access.cend]
+namespace ranges {
+namespace __cend {
+struct __fn {
+#  if _LIBCPP_STD_VER >= 23
+  template <class _Rng>
+  using _UType = decltype(ranges::end(ranges::__possibly_const_range(std::declval<_Rng>())));
+
+  template <__const_accessible_range _Rng>
+  auto operator()(_Rng&& __rng) noexcept(noexcept(const_sentinel<_UType<_Rng>>(
+      ranges::end(ranges::__possibly_const_range(__rng))))) -> const_sentinel<_UType<_Rng>> {
+    return const_sentinel<_UType<_Rng>>(ranges::end(ranges::__possibly_const_range(__rng)));
+  }
+#  else // ^^^ _LIBCPP_STD_VER >= 23 / _LIBCPP_STD_VER < 23 vvv
+  template <class _Tp>
+    requires is_lvalue_reference_v<_Tp&&>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::end(static_cast<const remove_reference_t<_Tp>&>(__t))))
+          -> decltype(ranges::end(static_cast<const remove_reference_t<_Tp>&>(__t))) {
+    return ranges::end(static_cast<const remove_reference_t<_Tp>&>(__t));
+  }
+
+  template <class _Tp>
+    requires is_rvalue_reference_v<_Tp&&>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const noexcept(
+      noexcept(ranges::end(static_cast<const _Tp&&>(__t)))) -> decltype(ranges::end(static_cast<const _Tp&&>(__t))) {
+    return ranges::end(static_cast<const _Tp&&>(__t));
+  }
+#  endif
+};
+} // namespace __cend
+
+inline namespace __cpo {
+inline constexpr auto cend = __cend::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+// [range.access.crbegin]
+namespace ranges {
+namespace __crbegin {
+struct __fn {
+#  if _LIBCPP_STD_VER >= 23
+  template <class _Rng>
+  using _UType = decltype(ranges::rbegin(ranges::__possibly_const_range(std::declval<_Rng>())));
+
+  template <__const_accessible_range _Rng>
+  auto operator()(_Rng&& __rng) noexcept(noexcept(const_iterator<_UType<_Rng>>(
+      ranges::rbegin(ranges::__possibly_const_range(__rng))))) -> const_iterator<_UType<_Rng>> {
+    return const_iterator<_UType<_Rng>>(ranges::rbegin(ranges::__possibly_const_range(__rng)));
+  }
+#  else  // ^^^ _LIBCPP_STD_VER >= 23 / _LIBCPP_STD_VER < 23
+  template <class _Tp>
+    requires is_lvalue_reference_v<_Tp&&>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::rbegin(static_cast<const remove_reference_t<_Tp>&>(__t))))
+          -> decltype(ranges::rbegin(static_cast<const remove_reference_t<_Tp>&>(__t))) {
+    return ranges::rbegin(static_cast<const remove_reference_t<_Tp>&>(__t));
+  }
+
+  template <class _Tp>
+    requires is_rvalue_reference_v<_Tp&&>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::rbegin(static_cast<const _Tp&&>(__t))))
+          -> decltype(ranges::rbegin(static_cast<const _Tp&&>(__t))) {
+    return ranges::rbegin(static_cast<const _Tp&&>(__t));
+  }
+#  endif // ^^^ _LIBCPP_STD_VER < 23
+};
+} // namespace __crbegin
+
+inline namespace __cpo {
+inline constexpr auto crbegin = __crbegin::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+// [range.access.crend]
+namespace ranges {
+namespace __crend {
+struct __fn {
+#  if _LIBCPP_STD_VER >= 23
+  template <class _Rng>
+  using _UType = decltype(ranges::rend(ranges::__possibly_const_range(std::declval<_Rng>())));
+
+  template <__const_accessible_range _Rng>
+  auto operator()(_Rng&& __rng) noexcept(noexcept(const_sentinel<_UType<_Rng>>(
+      ranges::rend(ranges::__possibly_const_range(__rng))))) -> const_sentinel<_UType<_Rng>> {
+    return const_sentinel<_UType<_Rng>>(ranges::rend(ranges::__possibly_const_range(__rng)));
+  }
+#  else  // ^^^ _LIBCPP_STD_VER >= 23 / _LIBCPP_STD_VER < 23 vvv
+  template <class _Tp>
+    requires is_lvalue_reference_v<_Tp&&>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::rend(static_cast<const remove_reference_t<_Tp>&>(__t))))
+          -> decltype(ranges::rend(static_cast<const remove_reference_t<_Tp>&>(__t))) {
+    return ranges::rend(static_cast<const remove_reference_t<_Tp>&>(__t));
+  }
+
+  template <class _Tp>
+    requires is_rvalue_reference_v<_Tp&&>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const noexcept(
+      noexcept(ranges::rend(static_cast<const _Tp&&>(__t)))) -> decltype(ranges::rend(static_cast<const _Tp&&>(__t))) {
+    return ranges::rend(static_cast<const _Tp&&>(__t));
+  }
+#  endif // ^^^ _LIBCPP_STD_VER < 23
+};
+} // namespace __crend
+
+inline namespace __cpo {
+inline constexpr auto crend = __crend::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 20
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___RANGES_CONST_ACCESS_H
diff --git a/libcxx/include/__ranges/rbegin.h b/libcxx/include/__ranges/rbegin.h
index 12e739e1a2b85..a6f7e2d9f1aea 100644
--- a/libcxx/include/__ranges/rbegin.h
+++ b/libcxx/include/__ranges/rbegin.h
@@ -85,34 +85,6 @@ inline constexpr auto rbegin = __rbegin::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
-// [range.access.crbegin]
-
-namespace ranges {
-namespace __crbegin {
-struct __fn {
-  template <class _Tp>
-    requires is_lvalue_reference_v<_Tp&&>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
-      noexcept(noexcept(ranges::rbegin(static_cast<const remove_reference_t<_Tp>&>(__t))))
-          -> decltype(ranges::rbegin(static_cast<const remove_reference_t<_Tp>&>(__t))) {
-    return ranges::rbegin(static_cast<const remove_reference_t<_Tp>&>(__t));
-  }
-
-  template <class _Tp>
-    requires is_rvalue_reference_v<_Tp&&>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
-      noexcept(noexcept(ranges::rbegin(static_cast<const _Tp&&>(__t))))
-          -> decltype(ranges::rbegin(static_cast<const _Tp&&>(__t))) {
-    return ranges::rbegin(static_cast<const _Tp&&>(__t));
-  }
-};
-} // namespace __crbegin
-
-inline namespace __cpo {
-inline constexpr auto crbegin = __crbegin::__fn{};
-} // namespace __cpo
-} // namespace ranges
-
 #endif // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__ranges/rend.h b/libcxx/include/__ranges/rend.h
index 02b4c5999a7eb..5171cc6cf3434 100644
--- a/libcxx/include/__ranges/rend.h
+++ b/libcxx/include/__ranges/rend.h
@@ -89,33 +89,6 @@ inline constexpr auto rend = __rend::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
-// [range.access.crend]
-
-namespace ranges {
-namespace __crend {
-struct __fn {
-  template <class _Tp>
-    requires is_lvalue_reference_v<_Tp&&>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
-      noexcept(noexcept(ranges::rend(static_cast<const remove_reference_t<_Tp>&>(__t))))
-          -> decltype(ranges::rend(static_cast<const remove_reference_t<_Tp>&>(__t))) {
-    return ranges::rend(static_cast<const remove_reference_t<_Tp>&>(__t));
-  }
-
-  template <class _Tp>
-    requires is_rvalue_reference_v<_Tp&&>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const noexcept(
-      noexcept(ranges::rend(static_cast<const _Tp&&>(__t)))) -> decltype(ranges::rend(static_cast<const _Tp&&>(__t))) {
-    return ranges::rend(static_cast<const _Tp&&>(__t));
-  }
-};
-} // namespace __crend
-
-inline namespace __cpo {
-inline constexpr auto crend = __crend::__fn{};
-} // namespace __cpo
-} // namespace ranges
-
 #endif // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/iterator b/libcxx/include/iterator
index fca75f0a19ed1..9a0c618f860cf 100644
--- a/libcxx/include/iterator
+++ b/libcxx/include/iterator
@@ -383,6 +383,31 @@ insert_iterator<Container> inserter(Container& x, typename Container::iterator i
 template <class Container>
 constexpr insert_iterator<Container> inserter(Container& x, ranges::iterator_t<Container> i);  // since C++20
 
+template<indirectly_readable I>
+using iter_const_reference_t = see below;                                       // since C++23
+template<input_iterator I>
+using const_iterator = see below;                                               // since C++23
+template<semiregular S>
+using const_sentinel = see below;                                               // since C++23
+
+template<input_iterator Iterator>
+class basic_const_iterator;                                                     // since C++23
+
+template<class T, common_with<T> U>
+  requires input_iterator<common_type_t<T, U>>
+struct common_type<basic_const_iterator<T>, U>;                                 // since C++23
+template<class T, common_with<T> U>
+  requires input_iterator<common_type_t<T, U>>
+struct common_type<U, basic_const_iterator<T>>;                                 // since C++23
+template<class T, common_with<T> U>
+  requires input_iterator<common_type_t<T, U>>
+struct common_type<basic_const_iterator<T>, basic_const_iterator<U>>;           // since C++23
+
+template<input_iterator I>
+constexpr const_iterator<I> make_const_iterator(I it);                          // since C++23
+template<semiregular S>
+constexpr const_sentinel<S> make_const_sentinel(S s);                           // since C++23
+
 template <class Iterator>
 class move_iterator {
 public:
@@ -726,6 +751,10 @@ template <class E> constexpr const E* data(initializer_list<E> il) noexcept;
 #  include <__iterator/unreachable_sentinel.h>
 #endif
 
+#if _LIBCPP_STD_VER >= 23
+#  include <__iterator/const_iterator.h>
+#endif
+
 #include <version>
 
 // standard-mandated includes
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index fa35874265de6..7961862085023 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -39,6 +39,8 @@ namespace std::ranges {
     using iterator_t = decltype(ranges::begin(declval<T&>()));
   template<range R>
     using sentinel_t = decltype(ranges::end(declval<R&>()));
+  template<range R>
+    using const_iterator_t = decltype(ranges::end(declval<R&>()));               // since C++23
   template<range R>
     using range_difference_t = iter_difference_t<iterator_t<R>>;
   template<sized_range R>
@@ -47,6 +49,8 @@ namespace std::ranges {
     using range_value_t = iter_value_t<iterator_t<R>>;
   template<range R>
     using range_reference_t = iter_reference_t<iterator_t<R>>;
+  template<range R>
+    using range_const_reference_t = decltype(ranges::end(declval<R&>()));        // since C++23
   template<range R>
     using range_rvalue_reference_t = iter_rvalue_reference_t<iterator_t<R>>;
   template <range R>
@@ -93,6 +97,9 @@ namespace std::ranges {
   template<class T>
   concept viewable_range = see below;
 
+  template<class T>
+    concept constant_range = see below;
+
   // [range.adaptor.object], range adaptor objects
   template<class D>
     requires is_class_v<D> && same_as<D, remove_cv_t<D>>
@@ -387,6 +394,7 @@ namespace std {
 #  include <__ranges/all.h>
 #  include <__ranges/common_view.h>
 #  include <__ranges/concepts.h>
+#  include <__ranges/const_access.h>
 #  include <__ranges/counted.h>
 #  include <__ranges/dangling.h>
 #  include <__ranges/data.h>
diff --git a/libcxx/include/span b/libcxx/include/span
index 60d76d830f0f3..72ac447b17091 100644
--- a/libcxx/include/span
+++ b/libcxx/include/span
@@ -65,7 +65,9 @@ public:
     using reference = element_type&;
     using const_reference = const element_type&;
     using iterator = implementation-defined;
+    using const_iterator = std::const_iterator<iterator>;               // since C++23
     using reverse_iterator = std::reverse_iterator<iterator>;
+    using const_reverse_iterator = std::const_iterator<iterator>;       // since C++23
     static constexpr size_type extent = Extent;
 
     // [span.cons], span constructors, copy, assignment, and destructor
@@ -115,8 +117,12 @@ public:
     // [span.iterators], span iterator support
     constexpr iterator begin() const noexcept;
     constexpr iterator end() const noexcept;
+    constexpr const_iterator cbegin() const noexcept { return begin(); }            // since C++23
+    constexpr const_iterator cend() const noexcept { return end(); }                // since C++23
     constexpr reverse_iterator rbegin() const noexcept;
     constexpr reverse_iterator rend() const noexcept;
+    constexpr const_reverse_iterator crbegin() const noexcept { return rbegin(); } // since C++23
+    constexpr const_reverse_iterator crend() const noexcept { return rend(); }     // since C++23
 
 private:
     pointer data_;    // exposition only
@@ -390,9 +396,28 @@ public:
     return iterator(data() + size());
 #  endif
   }
+
+#if _LIBCPP_STD_VER >= 23
+  _LIBCPP_HIDE_FROM_ABI constexpr const_iterator cbegin() const noexcept {
+    return begin();
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr const_iterator cend() const noexcept {
+    return end();
+  }
+#endif // _LIBCPP_STD_VER >= 23
+
   _LIBCPP_HIDE_FROM_ABI constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator(end()); }
   _LIBCPP_HIDE_FROM_ABI constexpr reverse_iterator rend() const noexcept { return reverse_iterator(begin()); }
 
+#if _LIBCPP_STD_VER >= 23
+  _LIBCPP_HIDE_FROM_ABI constexpr const_reverse_iterator crbegin() const noexcept {
+    return rbegin();
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr const_reverse_iterator crend() const noexcept {
+    return rend();
+  }
+#endif // _LIBCPP_STD_VER >= 23
+
   _LIBCPP_HIDE_FROM_ABI span<const byte, _Extent * sizeof(element_type)> __as_bytes() const noexcept {
     return span<const byte, _Extent * sizeof(element_type)>{reinterpret_cast<const byte*>(data()), size_bytes()};
   }



More information about the libcxx-commits mailing list