[libcxx-commits] [libcxx] [libc++] Collapse `optional<T&>` inheritance hierarchy (PR #215286)

William Tran-Viet via libcxx-commits libcxx-commits at lists.llvm.org
Mon Aug 10 21:34:17 PDT 2026


https://github.com/smallp-o-p updated https://github.com/llvm/llvm-project/pull/215286

>From ada22d36e9a082d81534efad22e9c5826c6361c7 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Mon, 10 Aug 2026 10:09:44 -0400
Subject: [PATCH 1/3] Collapse `optional<T&>` inheritance hierarchy

---
 libcxx/include/__optional/optional_ref.h | 145 ++++++++++-------------
 1 file changed, 60 insertions(+), 85 deletions(-)

diff --git a/libcxx/include/__optional/optional_ref.h b/libcxx/include/__optional/optional_ref.h
index 9207d22f8f9b7..b1b67bd64a85f 100644
--- a/libcxx/include/__optional/optional_ref.h
+++ b/libcxx/include/__optional/optional_ref.h
@@ -54,12 +54,31 @@ _LIBCPP_PUSH_MACROS
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _Tp>
-struct __optional_ref_base {
-  using value_type                 = _Tp;
-  using __raw_type _LIBCPP_NODEBUG = remove_reference_t<_Tp>;
-  __raw_type* __value_;
+struct __optional_ref_iterator_base {};
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __optional_ref_base() noexcept : __value_(nullptr) {}
+#  if _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+
+template <class _Tp>
+  requires(is_object_v<_Tp> && !__is_unbounded_array_v<_Tp>)
+struct __optional_ref_iterator_base<_Tp&> {
+#    ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+  using iterator = __bounded_iter<_Tp*>;
+#    else
+  using iterator = __capacity_aware_iterator<_Tp*, optional<_Tp&>, 1>;
+#    endif
+};
+
+#  endif
+
+template <class _Tp>
+class optional<_Tp&> : public __optional_ref_iterator_base<_Tp&> {
+public:
+  using value_type = _Tp;
+  static_assert(!is_same_v<remove_cv_t<_Tp>, in_place_t>, "instantiation of optional with in_place_t is ill-formed");
+  static_assert(!is_same_v<remove_cv_t<_Tp>, nullopt_t>, "instantiation of optional with nullopt_t is ill-formed");
+
+private:
+  _Tp* __value_{nullptr};
 
   template <class _Up>
   _LIBCPP_HIDE_FROM_ABI constexpr void __convert_init_ref_val(_Up&& __val) {
@@ -67,27 +86,15 @@ struct __optional_ref_base {
     __value_ = std::addressof(__r);
   }
 
-  template <class _UArg>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_ref_base(in_place_t, _UArg&& __uarg) {
-    static_assert(!__reference_constructs_from_temporary_v<_Tp, _UArg>,
-                  "Attempted to construct a reference element in optional from a "
-                  "possible temporary");
-    __convert_init_ref_val(std::forward<_UArg>(__uarg));
-  }
+  template <class _Up, class _QualUp>
+  static constexpr bool __check_optionalU_ctor =
+      !is_same_v<remove_cv_t<_Tp>, optional<_Up>> && !is_same_v<_Tp&, _Up> && is_constructible_v<_Tp&, _QualUp>;
 
-#  if _LIBCPP_STD_VER >= 23
   template <class _Fp, class... _Args>
-  constexpr __optional_ref_base(__optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args) {
+  constexpr optional(__optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args) {
     __convert_init_ref_val(std::forward<invoke_result_t<_Fp, _Args...>>(
         std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...)));
   }
-#  endif
-
-  _LIBCPP_HIDE_FROM_ABI constexpr void reset() noexcept { __value_ = nullptr; }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return __value_ != nullptr; }
-
-  _LIBCPP_HIDE_FROM_ABI constexpr value_type& __get() const noexcept { return *__value_; }
 
   template <class _UArg>
   _LIBCPP_HIDE_FROM_ABI constexpr void __construct(_UArg&& __val) {
@@ -102,75 +109,28 @@ struct __optional_ref_base {
     if (__opt.has_value())
       __construct(std::forward<_That>(__opt).__get());
   }
-};
 
-template <class _Tp>
-struct __optional_ref_iterator_base : __optional_ref_base<_Tp&> {
-  using __optional_ref_base<_Tp&>::__optional_ref_base;
-};
-
-#  if _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
-
-template <class _Tp>
-  requires(is_object_v<_Tp> && !__is_unbounded_array_v<_Tp>)
-struct __optional_ref_iterator_base<_Tp&> : __optional_ref_base<_Tp&> {
-private:
-  using __pointer _LIBCPP_NODEBUG = add_pointer_t<_Tp>;
+  static constexpr bool __has_iterator_ = requires { typename __optional_ref_iterator_base<_Tp&>::iterator; };
 
 public:
-  using __optional_ref_base<_Tp&>::__optional_ref_base;
-
-#    ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
-  using iterator = __bounded_iter<__pointer>;
-#    else
-  using iterator = __capacity_aware_iterator<__pointer, optional<_Tp&>, 1>;
-#    endif
-
-  // [optional.ref.iterators], iterator support
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const noexcept {
-    auto* __ptr = this->has_value() ? std::addressof(this->__get()) : nullptr;
-
-#    ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
-    return std::__make_bounded_iter(__ptr, __ptr, __ptr + (this->has_value() ? 1 : 0));
-#    else
-    return std::__make_capacity_aware_iterator<__pointer, optional<_Tp&>, 1>(__ptr);
-#    endif
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const noexcept {
-    return begin() + (this->has_value() ? 1 : 0);
-  }
-};
-
-#  endif
-
-template <class _Tp>
-class optional<_Tp&> : public __optional_ref_iterator_base<_Tp&> {
-  using __base _LIBCPP_NODEBUG = __optional_ref_iterator_base<_Tp&>;
-
-  template <class _Up, class _QualUp>
-  static constexpr bool __check_optionalU_ctor =
-      !is_same_v<remove_cv_t<_Tp>, optional<_Up>> && !is_same_v<_Tp&, _Up> && is_constructible_v<_Tp&, _QualUp>;
-  static_assert(!is_same_v<remove_cv_t<_Tp>, in_place_t>, "instantiation of optional with in_place_t is ill-formed");
-  static_assert(!is_same_v<remove_cv_t<_Tp>, nullopt_t>, "instantiation of optional with nullopt_t is ill-formed");
-
-public:
-  using value_type = _Tp;
-
   constexpr optional() noexcept = default;
   constexpr optional(nullopt_t) noexcept {}
   constexpr optional(const optional&) noexcept = default;
 
   template <class _Arg>
     requires(is_constructible_v<_Tp&, _Arg> && !reference_constructs_from_temporary_v<_Tp&, _Arg>)
-  constexpr explicit optional(in_place_t, _Arg&& __arg) : __base(in_place, std::forward<_Arg>(__arg)) {}
+  constexpr explicit optional(in_place_t, _Arg&& __arg) {
+    static_assert(!__reference_constructs_from_temporary_v<_Tp, _Arg>,
+                  "Attempted to construct a reference element in optional from a "
+                  "possible temporary");
+    __convert_init_ref_val(std::forward<_Arg>(__arg));
+  }
 
   template <class _Up>
     requires(!is_same_v<remove_cvref_t<_Up>, optional> && !is_same_v<remove_cvref_t<_Up>, in_place_t> &&
              is_constructible_v<_Tp&, _Up> && !reference_constructs_from_temporary_v<_Tp&, _Up>)
   constexpr explicit(!is_convertible_v<_Up, _Tp&>) optional(_Up&& __v) noexcept(is_nothrow_constructible_v<_Tp&, _Up>)
-      : __base(in_place, std::forward<_Up>(__v)) {}
+      : optional(in_place, std::forward<_Up>(__v)) {}
 
   template <class _Up>
     requires(__check_optionalU_ctor<_Up, _Up&> && !reference_constructs_from_temporary_v<_Tp&, _Up&>)
@@ -200,11 +160,6 @@ class optional<_Tp&> : public __optional_ref_iterator_base<_Tp&> {
     this->__construct_from(std::move(__rhs));
   }
 
-  template <class _Tag, class _Fp, class... _Args>
-    requires(is_same_v<_Tag, __optional_construct_from_invoke_tag>)
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_Tag, _Fp&& __f, _Args&&... __args)
-      : __base(__optional_construct_from_invoke_tag{}, std::forward<_Fp>(__f), std::forward<_Args>(__args)...) {}
-
   // deleted overloads
 
   template <class _Up>
@@ -235,7 +190,7 @@ class optional<_Tp&> : public __optional_ref_iterator_base<_Tp&> {
 
   constexpr ~optional() = default;
 
-  using __base::__get;
+  _LIBCPP_HIDE_FROM_ABI constexpr value_type& __get() const noexcept { return *__value_; }
 
   _LIBCPP_HIDE_FROM_ABI constexpr optional& operator=(nullopt_t) noexcept {
     reset();
@@ -257,7 +212,7 @@ class optional<_Tp&> : public __optional_ref_iterator_base<_Tp&> {
   // [optional.ref.observe]
   _LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<_Tp> operator->() const noexcept {
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator-> called on a disengaged value");
-    return std::addressof(this->__get());
+    return __value_;
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() const noexcept {
@@ -267,7 +222,7 @@ class optional<_Tp&> : public __optional_ref_iterator_base<_Tp&> {
 
   constexpr explicit operator bool() const noexcept { return has_value(); }
 
-  using __base::has_value;
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return __value_ != nullptr; }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& value() const {
     if (!this->has_value())
@@ -317,7 +272,27 @@ class optional<_Tp&> : public __optional_ref_iterator_base<_Tp&> {
     return std::forward<_Func>(__f)();
   }
 
-  using __base::reset;
+  _LIBCPP_HIDE_FROM_ABI constexpr void reset() noexcept { __value_ = nullptr; }
+
+  // [optional.ref.iterators], iterator support
+#  if _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const noexcept
+    requires __has_iterator_
+  {
+#    ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+    return std::__make_bounded_iter(__value_, __value_, __value_ + (this->has_value() ? 1 : 0));
+#    else
+    return std::__make_capacity_aware_iterator<_Tp*, optional<_Tp&>, 1>(__value_);
+#    endif
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const noexcept
+    requires __has_iterator_
+  {
+    return begin() + (this->has_value() ? 1 : 0);
+  }
+#  endif
 };
 
 _LIBCPP_END_NAMESPACE_STD

>From f955e27bb86ff088a7689a4e17135ac63af536c2 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Mon, 10 Aug 2026 15:28:19 -0400
Subject: [PATCH 2/3] Add empty bases declspec

---
 libcxx/include/__optional/optional_ref.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__optional/optional_ref.h b/libcxx/include/__optional/optional_ref.h
index b1b67bd64a85f..f379b097ffcb3 100644
--- a/libcxx/include/__optional/optional_ref.h
+++ b/libcxx/include/__optional/optional_ref.h
@@ -71,7 +71,7 @@ struct __optional_ref_iterator_base<_Tp&> {
 #  endif
 
 template <class _Tp>
-class optional<_Tp&> : public __optional_ref_iterator_base<_Tp&> {
+class _LIBCPP_DECLSPEC_EMPTY_BASES optional<_Tp&> : public __optional_ref_iterator_base<_Tp&> {
 public:
   using value_type = _Tp;
   static_assert(!is_same_v<remove_cv_t<_Tp>, in_place_t>, "instantiation of optional with in_place_t is ill-formed");

>From a6f4825fd9f94873a10d96b74de87ee6be80ee77 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Tue, 11 Aug 2026 00:33:56 -0400
Subject: [PATCH 3/3] Revert "Add empty bases declspec"

This reverts commit f955e27bb86ff088a7689a4e17135ac63af536c2.
---
 libcxx/include/__optional/optional_ref.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__optional/optional_ref.h b/libcxx/include/__optional/optional_ref.h
index f379b097ffcb3..b1b67bd64a85f 100644
--- a/libcxx/include/__optional/optional_ref.h
+++ b/libcxx/include/__optional/optional_ref.h
@@ -71,7 +71,7 @@ struct __optional_ref_iterator_base<_Tp&> {
 #  endif
 
 template <class _Tp>
-class _LIBCPP_DECLSPEC_EMPTY_BASES optional<_Tp&> : public __optional_ref_iterator_base<_Tp&> {
+class optional<_Tp&> : public __optional_ref_iterator_base<_Tp&> {
 public:
   using value_type = _Tp;
   static_assert(!is_same_v<remove_cv_t<_Tp>, in_place_t>, "instantiation of optional with in_place_t is ill-formed");



More information about the libcxx-commits mailing list