This implements changes from P3913R1.

For opt of type optional<T> is view (T is not const), the views::reverse(opt),
views::take(opt, n), views::drop(opt, n) now returns optional<T>.
In case of views::as_const, the optional<T&> is converted into optional<const 
T&>,
we do not return optional<T const> in non-reference case, as the later
is not a view.

libstdc++-v3/ChangeLog:

        * include/std/optional (__is_optional_ref): Define.
        * include/std/ranges (views::__detail::__is_optional_and_view):
        Define.
        (_Take::operator(), _Drop::operator(), _Reverse::operator()):
        Handle optional<T> that are view (T is non-const).
        (_AsConst::operator()): Handle optional<T&>.
        * testsuite/20_util/optional/range.cc: New tests.
---
Tested on x86_64-linux locally.

 libstdc++-v3/include/std/optional             |   8 +-
 libstdc++-v3/include/std/ranges               |  25 ++++
 .../testsuite/20_util/optional/range.cc       | 114 ++++++++++++++++++
 3 files changed, 146 insertions(+), 1 deletion(-)

diff --git a/libstdc++-v3/include/std/optional 
b/libstdc++-v3/include/std/optional
index 75a9531ccd5..d3ea40f7c75 100644
--- a/libstdc++-v3/include/std/optional
+++ b/libstdc++-v3/include/std/optional
@@ -1486,6 +1486,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     class optional<_Tp&>;
 
+  template<typename _Tp>
+    constexpr bool __is_optional_ref = false;
+
+  template<typename _Tp>
+    constexpr bool __is_optional_ref<optional<_Tp&>> = true;
+
   template<typename _Tp>
     struct __optional_ref_base
     {};
@@ -2187,7 +2193,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     constexpr bool
     ranges::enable_borrowed_range<optional<_Tp&>> = true;
 #endif
- 
+
   template<typename _Tp>
     inline constexpr range_format
       format_kind<optional<_Tp>> = range_format::disabled;
diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index 158692d92a7..32e450e3d1f 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -2391,6 +2391,15 @@ namespace views::__adaptor
       template<typename _Range, typename _Dp>
        concept __can_take_view
          = requires { take_view(std::declval<_Range>(), std::declval<_Dp>()); 
};
+
+#ifdef __cpp_lib_optional_range_support // >= C++26
+    template<typename _Tp>
+      constexpr bool __is_optional_and_view = false;
+
+    template<typename _Tp>
+      // optional<const T> is not assignable, so it does not model view.       
    
+      constexpr bool __is_optional_and_view<optional<_Tp>> = !is_const_v<_Tp>;
+#endif
     } // namespace __detail
 
     struct _Take : __adaptor::_RangeAdaptor<_Take>
@@ -2403,6 +2412,10 @@ namespace views::__adaptor
          using _Tp = remove_cvref_t<_Range>;
          if constexpr (__detail::__is_empty_view<_Tp>)
            return _Tp();
+#ifdef __cpp_lib_optional_range_support // >= C++26
+         else if constexpr (__detail::__is_optional_and_view<_Tp>)
+           return __n ? std::forward<_Range>(__r) : _Tp();
+#endif
          else if constexpr (random_access_range<_Tp>
                             && sized_range<_Tp>
                             && (std::__detail::__is_span<_Tp>
@@ -2679,6 +2692,10 @@ namespace views::__adaptor
          using _Tp = remove_cvref_t<_Range>;
          if constexpr (__detail::__is_empty_view<_Tp>)
            return _Tp();
+#ifdef __cpp_lib_optional_range_support // >= C++26
+         else if constexpr (__detail::__is_optional_and_view<_Tp>)
+           return __n ? _Tp() : std::forward<_Range>(__r);
+#endif
          else if constexpr (random_access_range<_Tp>
                             && sized_range<_Tp>
                             && (std::__detail::__is_span<_Tp>
@@ -4156,6 +4173,10 @@ namespace views::__adaptor
          using _Tp = remove_cvref_t<_Range>;
          if constexpr (__detail::__is_reverse_view<_Tp>)
            return std::forward<_Range>(__r).base();
+#ifdef __cpp_lib_optional_range_support // >= C++26
+         else if constexpr (__detail::__is_optional_and_view<_Tp>)
+           return std::forward<_Range>(__r);
+#endif
          else if constexpr (__detail::__is_reversible_subrange<_Tp>)
            {
              using _Iter = decltype(ranges::begin(__r).base());
@@ -9312,6 +9333,10 @@ namespace views::__adaptor
          return views::all(std::forward<_Range>(__r));
        else if constexpr (__detail::__is_empty_view<_Tp>)
          return views::empty<const element_type>;
+#if __cpp_lib_optional >= 202506L && __cpp_lib_optional_range_support // >= 
C++26
+       else if constexpr (__is_optional_ref<_Tp>)
+         return optional<const typename _Tp::value_type&>(__r);
+#endif
        else if constexpr (std::__detail::__is_span<_Tp>)
          return span<const element_type, 
_Tp::extent>(std::forward<_Range>(__r));
        else if constexpr (__detail::__is_constable_ref_view<_Tp>)
diff --git a/libstdc++-v3/testsuite/20_util/optional/range.cc 
b/libstdc++-v3/testsuite/20_util/optional/range.cc
index 981969cb614..6e5231668a1 100644
--- a/libstdc++-v3/testsuite/20_util/optional/range.cc
+++ b/libstdc++-v3/testsuite/20_util/optional/range.cc
@@ -168,6 +168,100 @@ constexpr void test_not_range()
   static_assert(!requires(std::optional<T> o) { o.end(); });
 };
 
+template<typename T>
+constexpr bool is_optional = false;
+
+template<typename T>
+constexpr bool is_optional<std::optional<T>> = true;
+
+template<bool usesOptional, typename T>
+constexpr void test_as_const(const T& t)
+{
+  std::optional<T> o(std::in_place, t);
+  auto cv = std::views::as_const(o);
+  static_assert(is_optional<decltype(cv)> == usesOptional);
+  static_assert(std::is_same_v<decltype(*cv.begin()), const 
std::remove_reference_t<T>&>);
+  VERIFY(!std::ranges::empty(cv));
+  VERIFY(*cv.begin() == t);
+      
+  std::optional<T> e;
+  auto cve = std::views::as_const(e);
+  static_assert(is_optional<decltype(cve)> == usesOptional);
+  static_assert(std::is_same_v<decltype(*cve.begin()), const 
std::remove_reference_t<T>&>);
+  VERIFY(std::ranges::empty(cve));
+}
+
+template<bool usesOptional, typename T>
+constexpr void test_reverse(const T& t)
+{
+  std::optional<T> o(std::in_place, t);
+  auto rv = std::views::reverse(o);
+  static_assert(is_optional<decltype(rv)> == usesOptional);
+  static_assert(std::is_same_v<decltype(*rv.begin()), T&>);
+  VERIFY(!std::ranges::empty(rv));
+  VERIFY(*rv.begin() == t);
+      
+  std::optional<T> e;
+  auto rve = std::views::reverse(e);
+  static_assert(is_optional<decltype(rve)> == usesOptional);
+  static_assert(std::is_same_v<decltype(*rve.begin()), T&>);
+  VERIFY(std::ranges::empty(rve));
+}
+
+template<bool usesOptional, typename T>
+constexpr void test_take(const T& t)
+{
+  std::optional<T> o(std::in_place, t);
+  auto tvp = std::views::take(o, 3);
+  static_assert(is_optional<decltype(tvp)> == usesOptional);
+  static_assert(std::is_same_v<decltype(*tvp.begin()), T&>);
+  VERIFY(!std::ranges::empty(tvp));
+  VERIFY(*tvp.begin() == t);
+
+  auto tvz = std::views::take(o, 0);
+  static_assert(is_optional<decltype(tvz)> == usesOptional);
+  static_assert(std::is_same_v<decltype(*tvz.begin()), T&>);
+  VERIFY(std::ranges::empty(tvz));
+
+  std::optional<T> e;
+  auto tvep = std::views::take(e, 5);
+  static_assert(is_optional<decltype(tvep)> == usesOptional);
+  static_assert(std::is_same_v<decltype(*tvep.begin()), T&>);
+  VERIFY(std::ranges::empty(tvep));
+
+  auto tvez = std::views::take(e, 0);
+  static_assert(is_optional<decltype(tvez)> == usesOptional);
+  static_assert(std::is_same_v<decltype(*tvez.begin()), T&>);
+  VERIFY(std::ranges::empty(tvez));
+}
+
+template<bool usesOptional, typename T>
+constexpr void test_drop(const T& t)
+{
+  std::optional<T> o(std::in_place, t);
+  auto dvp = std::views::drop(o, 3);
+  static_assert(is_optional<decltype(dvp)> == usesOptional);
+  static_assert(std::is_same_v<decltype(*dvp.begin()), T&>);
+  VERIFY(std::ranges::empty(dvp));
+
+  auto dvz = std::views::drop(o, 0);
+  static_assert(is_optional<decltype(dvz)> == usesOptional);
+  static_assert(std::is_same_v<decltype(*dvz.begin()), T&>);
+  VERIFY(!std::ranges::empty(dvz));
+  VERIFY(*dvz.begin() == t);
+
+  std::optional<T> e;
+  auto dvep = std::views::drop(e, 5);
+  static_assert(is_optional<decltype(dvep)> == usesOptional);
+  static_assert(std::is_same_v<decltype(*dvep.begin()), T&>);
+  VERIFY(std::ranges::empty(dvep));
+
+  auto dvez = std::views::drop(e, 0);
+  static_assert(is_optional<decltype(dvez)> == usesOptional);
+  static_assert(std::is_same_v<decltype(*dvez.begin()), T&>);
+  VERIFY(std::ranges::empty(dvez));
+}
+
 constexpr
 bool
 all_tests()
@@ -192,6 +286,26 @@ all_tests()
 
   range_chain_example();
 
+  test_as_const<false, int>(i);
+  test_as_const<false, const int>(i);
+  test_as_const<true, int&>(i);
+  test_as_const<true, const int&>(i);
+
+  test_reverse<true, int>(i);
+  test_reverse<false, const int>(i);
+  test_reverse<true, int&>(i);
+  test_reverse<true, const int&>(i);
+
+  test_take<true, int>(i);
+  test_take<false, const int>(i);
+  test_take<true, int&>(i);
+  test_take<true, const int&>(i);
+
+  test_drop<true, int>(i);
+  test_drop<false, const int>(i);
+  test_drop<true, int&>(i);
+  test_drop<true, const int&>(i);
+
   return true;
 }
 
-- 
2.51.0

Reply via email to