[libcxx-commits] [libcxx] [libc++] Collapse `optional<T&>` inheritance hierarchy (PR #215286)
William Tran-Viet via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Aug 11 06:01:13 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/4] 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/4] 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/4] 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");
>From 6115857bdb8f061d555efeee3df22ce88ad484fa Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Tue, 11 Aug 2026 09:01:01 -0400
Subject: [PATCH 4/4] Update optional_ref.h
Co-authored-by: Nikolas Klauser <nikolasklauser at berlin.de>
---
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..2fa8c474212e7 100644
--- a/libcxx/include/__optional/optional_ref.h
+++ b/libcxx/include/__optional/optional_ref.h
@@ -78,7 +78,7 @@ class optional<_Tp&> : public __optional_ref_iterator_base<_Tp&> {
static_assert(!is_same_v<remove_cv_t<_Tp>, nullopt_t>, "instantiation of optional with nullopt_t is ill-formed");
private:
- _Tp* __value_{nullptr};
+ _Tp* __value_ = nullptr;
template <class _Up>
_LIBCPP_HIDE_FROM_ABI constexpr void __convert_init_ref_val(_Up&& __val) {
More information about the libcxx-commits
mailing list