[libcxx-commits] [libcxx] [libc++] Reimplement optional<T&> as a partial specialization (PR #201983)
William Tran-Viet via libcxx-commits
libcxx-commits at lists.llvm.org
Fri Jun 5 18:59:43 PDT 2026
https://github.com/smallp-o-p updated https://github.com/llvm/llvm-project/pull/201983
>From c682ab4917fecf2f6dd56bd899312cce08a49443 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Wed, 20 May 2026 20:14:31 -0400
Subject: [PATCH 1/2] Reimplement optional<T&> as a partial specialization
---
libcxx/include/optional | 355 +++++++++---------
.../optional.monadic/and_then.pass.cpp | 37 +-
.../optional.monadic/or_else.pass.cpp | 8 +
.../optional.monadic/transform.pass.cpp | 21 +-
.../optional_in_place_t.verify.cpp | 8 +-
.../optional_nullopt_t.verify.cpp | 11 +-
6 files changed, 250 insertions(+), 190 deletions(-)
diff --git a/libcxx/include/optional b/libcxx/include/optional
index d0ddd2c0b8ea2..a5a98425c88ae 100644
--- a/libcxx/include/optional
+++ b/libcxx/include/optional
@@ -558,7 +558,7 @@ struct __optional_storage_base<_Tp, true> {
_LIBCPP_HIDE_FROM_ABI constexpr __optional_storage_base() noexcept : __value_(nullptr) {}
template <class _Up>
- _LIBCPP_HIDE_FROM_ABI constexpr void __convert_init_ref_val(_Up&& __val) noexcept {
+ _LIBCPP_HIDE_FROM_ABI constexpr void __convert_init_ref_val(_Up&& __val) {
_Tp& __r(std::forward<_Up>(__val));
__value_ = std::addressof(__r);
}
@@ -571,6 +571,14 @@ struct __optional_storage_base<_Tp, true> {
__convert_init_ref_val(std::forward<_UArg>(__uarg));
}
+# if _LIBCPP_STD_VER >= 23
+ template <class _Fp, class... _Args>
+ constexpr __optional_storage_base(__optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args) {
+ _Tp& __r = std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...);
+ __value_ = std::addressof(__r);
+ }
+# endif
+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void reset() noexcept { __value_ = nullptr; }
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return __value_ != nullptr; }
@@ -579,7 +587,6 @@ struct __optional_storage_base<_Tp, true> {
template <class _UArg>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __construct(_UArg&& __val) {
- _LIBCPP_ASSERT_INTERNAL(!has_value(), "__construct called for engaged __optional_storage");
static_assert(!__reference_constructs_from_temporary_v<_Tp, _UArg>,
"Attempted to construct a reference element in tuple from a "
"possible temporary");
@@ -726,8 +733,8 @@ using __optional_sfinae_ctor_base_t _LIBCPP_NODEBUG =
template <class _Tp>
using __optional_sfinae_assign_base_t _LIBCPP_NODEBUG =
- __sfinae_assign_base< (is_copy_constructible_v<_Tp> && is_copy_assignable_v<_Tp>) || is_lvalue_reference_v<_Tp>,
- (is_move_constructible_v<_Tp> && is_move_assignable_v<_Tp>) || is_lvalue_reference_v<_Tp>>;
+ __sfinae_assign_base< (is_copy_constructible_v<_Tp> && is_copy_assignable_v<_Tp>),
+ (is_move_constructible_v<_Tp> && is_move_assignable_v<_Tp>)>;
template <class _Tp>
class optional;
@@ -769,6 +776,7 @@ inline constexpr bool __is_constructible_for_optional_initializer_list_v =
# if _LIBCPP_STD_VER >= 26
template <class _Tp, class... _Args>
inline constexpr bool __is_constructible_for_optional_v<_Tp&, _Args...> = false;
+
template <class _Tp, class _Arg>
inline constexpr bool __is_constructible_for_optional_v<_Tp&, _Arg> =
is_constructible_v<_Tp&, _Arg> && !reference_constructs_from_temporary_v<_Tp&, _Arg>;
@@ -777,6 +785,14 @@ template <class _Tp, class _Up, class... _Args>
inline constexpr bool __is_constructible_for_optional_initializer_list_v<_Tp&, _Up, _Args...> = false;
# endif
+# if _LIBCPP_STD_VER >= 26
+template <class _Tp>
+inline constexpr bool __is_valid_optional_type = std::is_object_v<_Tp> || std::is_lvalue_reference_v<_Tp>;
+# else
+template <class _Tp>
+inline constexpr bool __is_valid_optional_type = std::is_object_v<_Tp>;
+# endif
+
template <class _Tp, class = void>
struct __optional_iterator_base : __optional_move_assign_base<_Tp> {
using __optional_move_assign_base<_Tp>::__optional_move_assign_base;
@@ -878,9 +894,8 @@ public:
conditional_t<__libcpp_is_trivially_relocatable<_Tp>::value, optional, void>;
private:
- static_assert(!is_same_v<__remove_cvref_t<_Tp>, in_place_t>,
- "instantiation of optional with in_place_t is ill-formed");
- static_assert(!is_same_v<__remove_cvref_t<_Tp>, nullopt_t>, "instantiation of optional with nullopt_t is ill-formed");
+ 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");
# if _LIBCPP_STD_VER >= 26
static_assert(!is_rvalue_reference_v<_Tp>, "instantiation of optional with an rvalue reference type is ill-formed");
# else
@@ -889,16 +904,6 @@ private:
static_assert(is_destructible_v<_Tp>, "instantiation of optional with a non-destructible type is ill-formed");
static_assert(!is_array_v<_Tp>, "instantiation of optional with an array type is ill-formed");
-# if _LIBCPP_STD_VER >= 26
- template <class _Up>
- constexpr static bool __libcpp_opt_ref_ctor_deleted =
- is_lvalue_reference_v<_Tp> && reference_constructs_from_temporary_v<_Tp, _Up>;
-
- template <class _Up>
- constexpr static bool __ref_ctor_enabled =
- is_lvalue_reference_v<_Tp> && !reference_constructs_from_temporary_v<_Tp, _Up>;
-# endif
-
// LWG2756: conditionally explicit conversion from _Up
struct _CheckOptionalArgsConstructor {
template <class _Up>
@@ -984,117 +989,34 @@ public:
: __base(in_place, __il, std::forward<_Args>(__args)...) {}
template <class _Up = _Tp, enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_implicit<_Up>(), int> = 0>
- _LIBCPP_HIDE_FROM_ABI constexpr optional(_Up&& __v)
-# if _LIBCPP_STD_VER >= 26
- noexcept(is_lvalue_reference_v<_Tp> && is_nothrow_constructible_v<_Tp&, _Up>)
-# endif
- : __base(in_place, std::forward<_Up>(__v)) {
- }
+ _LIBCPP_HIDE_FROM_ABI constexpr optional(_Up&& __v) : __base(in_place, std::forward<_Up>(__v)) {}
template <class _Up = remove_cv_t<_Tp>,
enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_explicit<_Up>(), int> = 0>
- _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_Up&& __v)
-# if _LIBCPP_STD_VER >= 26
- noexcept(is_lvalue_reference_v<_Tp> && is_nothrow_constructible_v<_Tp&, _Up>)
-# endif
- : __base(in_place, std::forward<_Up>(__v)) {
- }
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_Up&& __v) : __base(in_place, std::forward<_Up>(__v)) {}
// LWG2756: conditionally explicit conversion from const optional<_Up>&
template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up const&>::template __enable_implicit<_Up>(), int> = 0>
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional(const optional<_Up>& __v)
-# if _LIBCPP_STD_VER >= 26
- noexcept(is_lvalue_reference_v<_Tp> && is_nothrow_constructible_v<_Tp&, const _Up&>)
-# endif
- {
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional(const optional<_Up>& __v) {
this->__construct_from(__v);
}
+
template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up const&>::template __enable_explicit<_Up>(), int> = 0>
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit optional(const optional<_Up>& __v)
-# if _LIBCPP_STD_VER >= 26
- noexcept(is_lvalue_reference_v<_Tp> && is_nothrow_constructible_v<_Tp&, const _Up&>)
-# endif
- {
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit optional(const optional<_Up>& __v) {
this->__construct_from(__v);
}
// LWG2756: conditionally explicit conversion from optional<_Up>&&
template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>::template __enable_implicit<_Up>(), int> = 0>
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional(optional<_Up>&& __v)
-# if _LIBCPP_STD_VER >= 26
- noexcept(is_lvalue_reference_v<_Tp> && is_nothrow_constructible_v<_Tp&, _Up>)
-# endif
- {
- this->__construct_from(std::move(__v));
- }
- template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>::template __enable_explicit<_Up>(), int> = 0>
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit optional(optional<_Up>&& __v)
-# if _LIBCPP_STD_VER >= 26
- noexcept(is_lvalue_reference_v<_Tp> && is_nothrow_constructible_v<_Tp&, _Up>)
-# endif
- {
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional(optional<_Up>&& __v) {
this->__construct_from(std::move(__v));
}
- // deleted optional<T&> constructors and additional optional<T&> constructors
-# if _LIBCPP_STD_VER >= 26
- // optional(U&&)
- template <class _Up = _Tp, enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_implicit<_Up>(), int> = 0>
- requires __libcpp_opt_ref_ctor_deleted<_Up>
- optional(_Up&&) = delete;
-
- template <class _Up = remove_cv_t<_Tp>,
- enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_explicit<_Up>(), int> = 0>
- requires __libcpp_opt_ref_ctor_deleted<_Up>
- explicit optional(_Up&&) = delete;
-
- // optional(optional<U>& rhs)
- template <class _Up>
- requires __ref_ctor_enabled<_Up&> && (!is_same_v<remove_cvref_t<_Tp>, optional<_Up>>) && (!is_same_v<_Tp&, _Up>) &&
- is_constructible_v<_Tp&, _Up&>
- _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_Up&, _Tp&>)
- optional(optional<_Up>& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, _Up&>) {
- this->__construct_from(__rhs);
- }
-
- template <class _Up>
- requires __libcpp_opt_ref_ctor_deleted<_Up&> && (!is_same_v<remove_cvref_t<_Tp>, optional<_Up>>) &&
- (!is_same_v<_Tp&, _Up>) && is_constructible_v<_Tp&, _Up&>
- constexpr explicit optional(optional<_Up>& __rhs) noexcept = delete;
-
- // optional(const optional<U>&)
- template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up const&>::template __enable_implicit<_Up>(), int> = 0>
- requires __libcpp_opt_ref_ctor_deleted<const _Up&>
- optional(const optional<_Up>&) = delete;
-
- template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up const&>::template __enable_explicit<_Up>(), int> = 0>
- requires __libcpp_opt_ref_ctor_deleted<const _Up&>
- explicit optional(const optional<_Up>&) = delete;
-
- // optional(optional<U>&&)
- template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>::template __enable_implicit<_Up>(), int> = 0>
- requires __libcpp_opt_ref_ctor_deleted<_Up>
- optional(optional<_Up>&&) = delete;
-
template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>::template __enable_explicit<_Up>(), int> = 0>
- requires __libcpp_opt_ref_ctor_deleted<_Up>
- explicit optional(optional<_Up>&&) = delete;
-
- // optional(const optional<U>&&)
- template <class _Up>
- requires __ref_ctor_enabled<const _Up> && (!is_same_v<remove_cvref_t<_Tp>, optional<_Up>>) &&
- (!is_same_v<_Tp&, _Up>) && is_constructible_v<_Tp&, const _Up>
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!is_convertible_v<const _Up, _Tp&>)
- optional(const optional<_Up>&& __v) noexcept(is_nothrow_constructible_v<_Tp&, const _Up>) {
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit optional(optional<_Up>&& __v) {
this->__construct_from(std::move(__v));
}
- template <class _Up>
- requires __libcpp_opt_ref_ctor_deleted<const _Up> && (!is_same_v<remove_cvref_t<_Tp>, optional<_Up>>) &&
- (!is_same_v<_Tp&, _Up>) && is_constructible_v<_Tp&, const _Up>
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional(const optional<_Up>&& __v) noexcept = delete;
-# endif
-
# if _LIBCPP_STD_VER >= 23
template <class _Tag,
class _Fp,
@@ -1117,8 +1039,7 @@ public:
enable_if_t<_And<_IsNotSame<__remove_cvref_t<_Up>, optional>,
_Or<_IsNotSame<__remove_cvref_t<_Up>, _Tp>, _Not<is_scalar<_Tp>>>,
is_constructible<_Tp, _Up>,
- is_assignable<_Tp&, _Up>,
- is_object<_Tp>>::value,
+ is_assignable<_Tp&, _Up>>::value,
int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional& operator=(_Up&& __v) {
if (this->has_value())
@@ -1143,11 +1064,7 @@ public:
}
template <class... _Args, enable_if_t<__is_constructible_for_optional_v<_Tp, _Args...>, int> = 0>
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& emplace(_Args&&... __args)
-# if _LIBCPP_STD_VER >= 26
- noexcept(is_lvalue_reference_v<_Tp> && is_nothrow_constructible_v<_Tp, _Args...>)
-# endif
- {
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& emplace(_Args&&... __args) {
reset();
this->__construct(std::forward<_Args>(__args)...);
return this->__get();
@@ -1162,12 +1079,8 @@ public:
return this->__get();
}
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void swap(optional& __opt) noexcept(
- (is_nothrow_move_constructible_v<_Tp> && is_nothrow_swappable_v<_Tp>)
-# if _LIBCPP_STD_VER >= 26
- || is_lvalue_reference_v<_Tp>
-# endif
- ) {
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
+ swap(optional& __opt) noexcept((is_nothrow_move_constructible_v<_Tp> && is_nothrow_swappable_v<_Tp>)) {
this->__swap(__opt);
}
@@ -1181,9 +1094,6 @@ public:
using __base::value;
template <class _Up = remove_cv_t<_Tp>>
-# if _LIBCPP_STD_VER >= 26
- requires(is_object_v<_Tp>)
-# endif
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) const& {
static_assert(is_copy_constructible_v<_Tp>, "optional<T>::value_or: T must be copy constructible");
static_assert(is_convertible_v<_Up, _Tp>, "optional<T>::value_or: U must be convertible to T");
@@ -1191,9 +1101,6 @@ public:
}
template <class _Up = remove_cv_t<_Tp>>
-# if _LIBCPP_STD_VER >= 26
- requires(is_object_v<_Tp>)
-# endif
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) && {
static_assert(is_move_constructible_v<_Tp>, "optional<T>::value_or: T must be move constructible");
static_assert(is_convertible_v<_Up, _Tp>, "optional<T>::value_or: U must be convertible to T");
@@ -1202,9 +1109,6 @@ public:
# if _LIBCPP_STD_VER >= 23
template <class _Func>
-# if _LIBCPP_STD_VER >= 26
- requires(is_object_v<_Tp>)
-# endif
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) & {
using _Up = invoke_result_t<_Func, _Tp&>;
static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
@@ -1215,9 +1119,6 @@ public:
}
template <class _Func>
-# if _LIBCPP_STD_VER >= 26
- requires(is_object_v<_Tp>)
-# endif
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const& {
using _Up = invoke_result_t<_Func, const _Tp&>;
static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
@@ -1228,9 +1129,6 @@ public:
}
template <class _Func>
-# if _LIBCPP_STD_VER >= 26
- requires(is_object_v<_Tp>)
-# endif
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) && {
using _Up = invoke_result_t<_Func, _Tp&&>;
static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
@@ -1241,9 +1139,6 @@ public:
}
template <class _Func>
-# if _LIBCPP_STD_VER >= 26
- requires(is_object_v<_Tp>)
-# endif
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const&& {
using _Up = invoke_result_t<_Func, const _Tp&&>;
static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
@@ -1254,60 +1149,48 @@ public:
}
template <class _Func>
-# if _LIBCPP_STD_VER >= 26
- requires(is_object_v<_Tp>)
-# endif
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) & {
using _Up = remove_cv_t<invoke_result_t<_Func, _Tp&>>;
static_assert(!is_array_v<_Up>, "Result of f(value()) should not be an Array");
static_assert(!is_same_v<_Up, in_place_t>, "Result of f(value()) should not be std::in_place_t");
static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(value()) should not be std::nullopt_t");
- static_assert(is_object_v<_Up>, "Result of f(value()) should be an object type");
+ static_assert(__is_valid_optional_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
if (*this)
return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), value());
return optional<_Up>();
}
template <class _Func>
-# if _LIBCPP_STD_VER >= 26
- requires(is_object_v<_Tp>)
-# endif
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) const& {
using _Up = remove_cv_t<invoke_result_t<_Func, const _Tp&>>;
static_assert(!is_array_v<_Up>, "Result of f(value()) should not be an Array");
static_assert(!is_same_v<_Up, in_place_t>, "Result of f(value()) should not be std::in_place_t");
static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(value()) should not be std::nullopt_t");
- static_assert(is_object_v<_Up>, "Result of f(value()) should be an object type");
+ static_assert(__is_valid_optional_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
if (*this)
return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), value());
return optional<_Up>();
}
template <class _Func>
-# if _LIBCPP_STD_VER >= 26
- requires(is_object_v<_Tp>)
-# endif
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) && {
using _Up = remove_cv_t<invoke_result_t<_Func, _Tp&&>>;
static_assert(!is_array_v<_Up>, "Result of f(std::move(value())) should not be an Array");
static_assert(!is_same_v<_Up, in_place_t>, "Result of f(std::move(value())) should not be std::in_place_t");
static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(std::move(value())) should not be std::nullopt_t");
- static_assert(is_object_v<_Up>, "Result of f(std::move(value())) should be an object type");
+ static_assert(__is_valid_optional_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
if (*this)
return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), std::move(value()));
return optional<_Up>();
}
template <class _Func>
-# if _LIBCPP_STD_VER >= 26
- requires(is_object_v<_Tp>)
-# endif
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) const&& {
using _Up = remove_cv_t<invoke_result_t<_Func, const _Tp&&>>;
static_assert(!is_array_v<_Up>, "Result of f(std::move(value())) should not be an Array");
static_assert(!is_same_v<_Up, in_place_t>, "Result of f(std::move(value())) should not be std::in_place_t");
static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(std::move(value())) should not be std::nullopt_t");
- static_assert(is_object_v<_Up>, "Result of f(std::move(value())) should be an object type");
+ static_assert(__is_valid_optional_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
if (*this)
return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), std::move(value()));
return optional<_Up>();
@@ -1316,9 +1199,6 @@ public:
template <invocable _Func>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional or_else(_Func&& __f) const&
requires is_copy_constructible_v<_Tp>
-# if _LIBCPP_STD_VER >= 26
- && (is_object_v<_Tp>)
-# endif
{
static_assert(is_same_v<remove_cvref_t<invoke_result_t<_Func>>, optional>,
"Result of f() should be the same type as this optional");
@@ -1330,9 +1210,6 @@ public:
template <invocable _Func>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional or_else(_Func&& __f) &&
requires is_move_constructible_v<_Tp>
-# if _LIBCPP_STD_VER >= 26
- && (is_object_v<_Tp>)
-# endif
{
static_assert(is_same_v<remove_cvref_t<invoke_result_t<_Func>>, optional>,
"Result of f() should be the same type as this optional");
@@ -1343,24 +1220,136 @@ public:
# endif // _LIBCPP_STD_VER >= 23
using __base::reset;
+};
-// optional<T&> overloads
# if _LIBCPP_STD_VER >= 26
+template <class _Tp>
+class optional<_Tp&> : public __optional_iterator_base<_Tp&> {
+ using __base _LIBCPP_NODEBUG = __optional_iterator_base<_Tp&>;
+
+ template <class _Up, class _QualUp>
+ static constexpr bool __check_optionalU_ctor =
+ !std::is_same_v<std::remove_cv_t<_Tp>, optional<_Up>> && !std::is_same_v<_Tp&, _Up> &&
+ std::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(std::is_constructible_v<_Tp&, _Arg> && !std::reference_constructs_from_temporary_v<_Tp&, _Arg>)
+ constexpr explicit optional(in_place_t, _Arg&& __arg) : __base(in_place, std::forward<_Arg>(__arg)) {}
+
+ template <class _Up>
+ requires(!std::is_same_v<std::remove_cvref_t<_Up>, optional> && !is_same_v<std::remove_cvref_t<_Up>, in_place_t> &&
+ is_constructible_v<_Tp&, _Up> && !std::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)) {}
+
+ template <class _Up>
+ requires(__check_optionalU_ctor<_Up, _Up&> && !std::reference_constructs_from_temporary_v<_Tp&, _Up&>)
+ constexpr explicit(!is_convertible_v<_Up&, _Tp&>)
+ optional(optional<_Up>& __rhs) noexcept(std::is_nothrow_constructible_v<_Tp&, _Up&>) {
+ this->__construct_from(__rhs);
+ }
+
+ template <class _Up>
+ requires(__check_optionalU_ctor<_Up, const _Up&> && !std::reference_constructs_from_temporary_v<_Tp&, const _Up&>)
+ constexpr explicit(!is_convertible_v<const _Up&, _Tp&>)
+ optional(const optional<_Up>& __rhs) noexcept(std::is_nothrow_constructible_v<_Tp&, const _Up&>) {
+ this->__construct_from(__rhs);
+ }
+
+ template <class _Up>
+ requires(__check_optionalU_ctor<_Up, _Up> && !std::reference_constructs_from_temporary_v<_Tp&, _Up>)
+ constexpr explicit(!is_convertible_v<_Up, _Tp&>)
+ optional(optional<_Up>&& __rhs) noexcept(std::is_nothrow_constructible_v<_Tp&, _Up>) {
+ this->__construct_from(std::move(__rhs));
+ }
+
+ template <class _Up>
+ requires(__check_optionalU_ctor<_Up, const _Up> && !std::reference_constructs_from_temporary_v<_Tp&, const _Up>)
+ constexpr explicit(!is_convertible_v<const _Up, _Tp&>)
+ optional(const optional<_Up>&& __rhs) noexcept(std::is_nothrow_constructible_v<_Tp&, const _Up>) {
+ this->__construct_from(std::move(__rhs));
+ }
+
+ template <class _Tag, class _Fp, class... _Args>
+ requires(std::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>
+ requires(!std::is_same_v<std::remove_cvref_t<_Up>, optional> && !is_same_v<std::remove_cvref_t<_Up>, in_place_t> &&
+ is_constructible_v<_Tp&, _Up> && std::reference_constructs_from_temporary_v<_Tp&, _Up>)
+ constexpr explicit(!is_convertible_v<_Up, _Tp&>)
+ optional(_Up&& __v) noexcept(is_nothrow_constructible_v<_Tp&, _Up>) = delete;
+
+ template <class _Up>
+ requires(__check_optionalU_ctor<_Up, _Up&> && std::reference_constructs_from_temporary_v<_Tp&, _Up&>)
+ constexpr explicit(!is_convertible_v<_Up&, _Tp&>)
+ optional(optional<_Up>& __rhs) noexcept(std::is_nothrow_constructible_v<_Tp&, _Up&>) = delete;
+
+ template <class _Up>
+ requires(__check_optionalU_ctor<_Up, const _Up&> && std::reference_constructs_from_temporary_v<_Tp&, const _Up&>)
+ constexpr explicit(!is_convertible_v<const _Up&, _Tp&>)
+ optional(const optional<_Up>& __rhs) noexcept(std::is_nothrow_constructible_v<_Tp&, const _Up&>) = delete;
+
+ template <class _Up>
+ requires(__check_optionalU_ctor<_Up, _Up> && std::reference_constructs_from_temporary_v<_Tp&, _Up>)
+ constexpr explicit(!is_convertible_v<_Up, _Tp&>)
+ optional(optional<_Up>&& __rhs) noexcept(std::is_nothrow_constructible_v<_Tp&, _Up>) = delete;
+
+ template <class _Up>
+ requires(__check_optionalU_ctor<_Up, const _Up> && std::reference_constructs_from_temporary_v<_Tp&, const _Up>)
+ constexpr explicit(!is_convertible_v<const _Up, _Tp&>)
+ optional(const optional<_Up>&& __rhs) noexcept(std::is_nothrow_constructible_v<_Tp&, const _Up>) = delete;
+
+ constexpr ~optional() = default;
+
+ using __base::__get;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr optional& operator=(nullopt_t) noexcept {
+ reset();
+ return *this;
+ }
+
+ constexpr optional& operator=(const optional&) noexcept = default;
+
+ template <class _Up>
+ requires(is_constructible_v<_Tp&, _Up> && !std::reference_constructs_from_temporary_v<_Tp&, _Up>)
+ constexpr _Tp& emplace(_Up&& __u) noexcept(std::is_nothrow_constructible_v<_Tp&, _Up>) {
+ this->__construct(std::forward<_Up>(__u));
+
+ return this->__get();
+ }
+
+ constexpr void swap(optional& __rhs) noexcept { this->__swap(__rhs); }
+
+ using __base::operator->;
+ using __base::operator*;
+ constexpr explicit operator bool() const noexcept { return has_value(); }
+ using __base::has_value;
+ using __base::value;
template <class _Up = remove_cvref_t<_Tp>>
- requires(is_lvalue_reference_v<_Tp> && is_object_v<__libcpp_remove_reference_t<_Tp>> &&
- !is_array_v<__libcpp_remove_reference_t<_Tp>>)
- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decay_t<_Tp> value_or(_Up&& __v) const {
- static_assert(
- is_constructible_v<remove_cvref_t<_Tp>, _Tp&>, "optional<T&>::value_or: remove_cv_t<T> must be constructible");
- static_assert(
- is_convertible_v<_Up, remove_cvref_t<_Tp>>, "optional<T&>::value_or: U must be convertible to remove_cv_t<T>");
- return this->has_value() ? this->__get() : static_cast<remove_cvref_t<_Tp>>(std::forward<_Up>(__v));
+ requires(!std::is_array_v<_Tp> && std::is_object_v<_Tp>)
+ [[nodiscard]] constexpr decay_t<_Tp> value_or(_Up&& __v) const {
+ using _XTp = std::remove_cv_t<_Tp>;
+ static_assert(std::is_constructible_v<_XTp, _Tp&>, "optional<T&>::value_or: remove_cv_t<T> must be constructible");
+ static_assert(std::is_convertible_v<_Up, _XTp>, "optional<T&>::value_or: U must be convertible to remove_cv_t<T>");
+ return this->has_value() ? this->__get() : static_cast<_XTp>(std::forward<_Up>(__v));
}
template <class _Func>
- requires(is_lvalue_reference_v<_Tp>)
- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const {
+ [[nodiscard]] constexpr auto and_then(_Func&& __f) const {
using _Up = invoke_result_t<_Func, _Tp&>;
static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
"Result of f(value()) must be a specialization of std::optional");
@@ -1370,30 +1359,30 @@ public:
}
template <class _Func>
- requires(is_lvalue_reference_v<_Tp>)
- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional<remove_cv_t<invoke_result_t<_Func, _Tp&>>>
- transform(_Func&& __f) const {
- using _Up = remove_cvref_t<invoke_result_t<_Func, _Tp&>>;
+ [[nodiscard]] constexpr optional<remove_cv_t<invoke_result_t<_Func, _Tp&>>> transform(_Func&& __f) const {
+ using _Up = remove_cv_t<invoke_result_t<_Func, _Tp&>>;
static_assert(!is_array_v<_Up>, "Result of f(value()) should not be an Array");
static_assert(!is_same_v<_Up, in_place_t>, "Result of f(value()) should not be std::in_place_t");
static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(value()) should not be std::nullopt_t");
- static_assert(is_object_v<_Up>, "Result of f(value()) should be an object type");
+ static_assert(__is_valid_optional_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
+
if (*this)
return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), value());
return optional<_Up>();
}
template <invocable _Func>
- requires(is_lvalue_reference_v<_Tp>)
- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional or_else(_Func&& __f) const {
+ [[nodiscard]] constexpr optional or_else(_Func&& __f) const {
static_assert(is_same_v<remove_cvref_t<invoke_result_t<_Func>>, optional>,
"Result of f() should be the same type as this optional");
if (*this)
return *this;
return std::forward<_Func>(__f)();
}
-# endif
+
+ using __base::reset;
};
+# endif
template <class _Tp>
optional(_Tp) -> optional<_Tp>;
@@ -1702,7 +1691,13 @@ operator<=>(const optional<_Tp>& __x, const _Up& __v) {
# endif // _LIBCPP_STD_VER >= 20
-template <class _Tp, enable_if_t< is_move_constructible_v<_Tp> && is_swappable_v<_Tp>, int> = 0>
+template <class _Tp,
+ enable_if_t<(is_move_constructible_v<_Tp> && is_swappable_v<_Tp>)
+# if _LIBCPP_STD_VER >= 26
+ || is_reference_v<_Tp>
+# endif
+ ,
+ int> = 0>
inline _LIBCPP_HIDE_FROM_ABI
_LIBCPP_CONSTEXPR_SINCE_CXX20 void swap(optional<_Tp>& __x, optional<_Tp>& __y) noexcept(noexcept(__x.swap(__y))) {
__x.swap(__y);
diff --git a/libcxx/test/std/utilities/optional/optional.monadic/and_then.pass.cpp b/libcxx/test/std/utilities/optional/optional.monadic/and_then.pass.cpp
index 93bbd74cd8678..e806cd8dd5343 100644
--- a/libcxx/test/std/utilities/optional/optional.monadic/and_then.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.monadic/and_then.pass.cpp
@@ -261,8 +261,6 @@ constexpr bool test() {
#if TEST_STD_VER >= 26
constexpr bool test_ref() {
- // Test "overloads", only the const (no ref-qualifier) and_then() should be called
-
{ // &
// Without & qualifier on F's operator()
{
@@ -339,6 +337,41 @@ constexpr bool test_ref() {
assert(std::move(i).and_then(std::move(nl)) == std::nullopt);
}
}
+
+ {
+ int i = 1;
+ int j = 2;
+ {
+ std::optional<int&> o(i);
+ std::same_as<std::optional<int&>> decltype(auto) r = o.and_then([&](auto&& ii) {
+ ii += j;
+ return std::optional<int&>(ii);
+ });
+
+ assert(i == 3);
+ assert(r == 3);
+ }
+ {
+ const std::optional<int&> o(i);
+ std::same_as<std::optional<int&>> decltype(auto) r = o.and_then([&](auto&& ii) {
+ ii += j;
+ return std::optional<int&>(ii);
+ });
+
+ assert(i == 5);
+ assert(r == 5);
+ }
+ {
+ std::optional<int&> o{};
+ std::same_as<std::optional<int>> decltype(auto) r = o.and_then([&](auto&&) { return std::optional(1); });
+ assert(r == std::nullopt);
+ }
+ {
+ const std::optional<int&> o{};
+ std::same_as<std::optional<int>> decltype(auto) r = o.and_then([&](auto&&) { return std::optional(1); });
+ assert(r == std::nullopt);
+ }
+ }
return true;
}
#endif
diff --git a/libcxx/test/std/utilities/optional/optional.monadic/or_else.pass.cpp b/libcxx/test/std/utilities/optional/optional.monadic/or_else.pass.cpp
index 326013cd6557f..7252f0a8700df 100644
--- a/libcxx/test/std/utilities/optional/optional.monadic/or_else.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.monadic/or_else.pass.cpp
@@ -104,6 +104,14 @@ constexpr bool test() {
}) == i);
}
+ {
+ int j = 2;
+ std::optional<int&> opt;
+ std::same_as<std::optional<int&>> decltype(auto) o = opt.or_else([&] { return std::optional<int&>(j); });
+ assert(o == j);
+ assert(&(*o) == &j);
+ }
+
#endif
return true;
diff --git a/libcxx/test/std/utilities/optional/optional.monadic/transform.pass.cpp b/libcxx/test/std/utilities/optional/optional.monadic/transform.pass.cpp
index b8536b790f749..96303c5796f6d 100644
--- a/libcxx/test/std/utilities/optional/optional.monadic/transform.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.monadic/transform.pass.cpp
@@ -204,8 +204,6 @@ constexpr bool test() {
#if TEST_STD_VER >= 26
constexpr bool test_ref() {
- // Test that no matter the ref qualifier on the object .transform() is invoked on, only the added
- // const (no ref-qualifier) overload is used
{
std::optional<int&> opt1;
std::same_as<std::optional<int>> decltype(auto) opt1r = opt1.transform([](int i) { return i + 2; });
@@ -229,7 +227,6 @@ constexpr bool test_ref() {
assert(*o2 == 4.0f);
}
- // &
{
// Without & qualifier on F's operator()
{
@@ -250,7 +247,6 @@ constexpr bool test_ref() {
assert(*o3 == 1);
}
}
- // const& overload
{
// Without & qualifier on F's operator()
{
@@ -308,6 +304,23 @@ constexpr bool test_ref() {
auto o6r = o6.transform([](int) { return 42; });
assert(!o6r);
}
+
+ {
+ int i = 42;
+ int j{43};
+
+ auto func = [&j](int&) -> int& { return j; };
+
+ std::optional<int&> opt{i};
+ std::same_as<std::optional<int&>> decltype(auto) o = opt.transform(func);
+ assert(o == j);
+ assert(&(*o) == &j);
+
+ std::same_as<std::optional<int&>> decltype(auto) o2 = std::as_const(opt).transform(func);
+ assert(o2 == j);
+ assert(&(*o2) == &j);
+ }
+
return true;
}
#endif
diff --git a/libcxx/test/std/utilities/optional/optional.syn/optional_in_place_t.verify.cpp b/libcxx/test/std/utilities/optional/optional.syn/optional_in_place_t.verify.cpp
index 20efef515c362..8731b0b37e9bc 100644
--- a/libcxx/test/std/utilities/optional/optional.syn/optional_in_place_t.verify.cpp
+++ b/libcxx/test/std/utilities/optional/optional.syn/optional_in_place_t.verify.cpp
@@ -15,7 +15,13 @@
#include <optional>
+#include "test_macros.h"
+
void f() {
std::optional<std::in_place_t> opt; // expected-note {{requested here}}
- // expected-error at optional:* {{instantiation of optional with in_place_t is ill-formed}}
+ // expected-error@*:* 1 {{instantiation of optional with in_place_t is ill-formed}}
+#if TEST_STD_VER >= 26
+ std::optional<std::in_place_t&> opt1; // expected-note {{requested here}}
+ // expected-error@*:* 1 {{instantiation of optional with in_place_t is ill-formed}}
+#endif
}
diff --git a/libcxx/test/std/utilities/optional/optional.syn/optional_nullopt_t.verify.cpp b/libcxx/test/std/utilities/optional/optional.syn/optional_nullopt_t.verify.cpp
index f807da7470ae9..0054b05aa767c 100644
--- a/libcxx/test/std/utilities/optional/optional.syn/optional_nullopt_t.verify.cpp
+++ b/libcxx/test/std/utilities/optional/optional.syn/optional_nullopt_t.verify.cpp
@@ -15,10 +15,15 @@
#include <optional>
+#include "test_macros.h"
+
void f() {
std::optional<std::nullopt_t> opt; // expected-note 1 {{requested here}}
std::optional<const std::nullopt_t> opt1; // expected-note 1 {{requested here}}
- std::optional<std::nullopt_t &> opt2; // expected-note 1 {{requested here}}
- std::optional<std::nullopt_t &&> opt3; // expected-note 1 {{requested here}}
- // expected-error at optional:* 4 {{instantiation of optional with nullopt_t is ill-formed}}
+ // expected-error@*:* 2 {{instantiation of optional with nullopt_t is ill-formed}}
+#if TEST_STD_VER >= 26
+ std::optional<std::nullopt_t&> opt2; // expected-note 1 {{requested here}}
+ std::optional<const std::nullopt_t&> opt3; // expected-note 1 {{requested here}}
+ // expected-error@*:* 2 {{instantiation of optional with nullopt_t is ill-formed}}
+#endif
}
>From 8e2749640288d6a61186b05d5b01e0e419be8a5c Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Fri, 5 Jun 2026 21:59:33 -0400
Subject: [PATCH 2/2] Missng test include
---
.../std/utilities/optional/optional.monadic/or_else.pass.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/libcxx/test/std/utilities/optional/optional.monadic/or_else.pass.cpp b/libcxx/test/std/utilities/optional/optional.monadic/or_else.pass.cpp
index 7252f0a8700df..3a06ce24bfc99 100644
--- a/libcxx/test/std/utilities/optional/optional.monadic/or_else.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.monadic/or_else.pass.cpp
@@ -15,6 +15,7 @@
#include "MoveOnly.h"
#include <cassert>
+#include <concepts>
#include <optional>
struct NonMovable {
More information about the libcxx-commits
mailing list