[libc++] Implement P2988R12: std::optional<T&> (#155202)

Resolves #148131

- Unlock `std::optional<T&>` implementation
- Allow instantiations of `optional<T(&)(...)>` and `optional<T(&)[]>`
but disables `value_or()` and `optional::iterator` + all `iterator`
related functions
- Update documentation
- Update tests
This commit is contained in:
William Tran-Viet 2025-11-11 22:00:08 -05:00 committed by GitHub
parent 95f2728b5c
commit 389a23c538
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
41 changed files with 1175 additions and 245 deletions

View File

@ -486,6 +486,8 @@ Status
---------------------------------------------------------- -----------------
``__cpp_lib_not_fn`` ``202306L``
---------------------------------------------------------- -----------------
``__cpp_lib_optional`` ``202506L``
---------------------------------------------------------- -----------------
``__cpp_lib_optional_range_support`` ``202406L``
---------------------------------------------------------- -----------------
``__cpp_lib_out_ptr`` ``202311L``

View File

@ -40,6 +40,7 @@ Implemented Papers
- P2321R2: ``zip`` (`Github <https://llvm.org/PR105169>`__) (The paper is partially implemented. ``zip_transform_view``
is implemented in this release)
- P2988R12: ``std::optional<T&>`` (`Github <https://llvm.org/PR148131>`__)
- P3044R2: sub-``string_view`` from ``string`` (`Github <https://llvm.org/PR148140>`__)
- P3223R2: Making ``std::istream::ignore`` less surprising (`Github <https://llvm.org/PR148178>`__)
- P3060R3: Add ``std::views::indices(n)`` (`Github <https://llvm.org/PR148175>`__)

View File

@ -122,7 +122,7 @@
"`P3293R3 <https://wg21.link/P3293R3>`__","Splicing a base class subobject","2025-06 (Sofia)","","","`#148125 <https://github.com/llvm/llvm-project/issues/148125>`__",""
"`P3491R3 <https://wg21.link/P3491R3>`__","``define_static_{string,object,array}``","2025-06 (Sofia)","","","`#148126 <https://github.com/llvm/llvm-project/issues/148126>`__",""
"`P3096R12 <https://wg21.link/P3096R12>`__","Function Parameter Reflection in Reflection for C++26","2025-06 (Sofia)","","","`#148127 <https://github.com/llvm/llvm-project/issues/148127>`__",""
"`P2988R12 <https://wg21.link/P2988R12>`__","``std::optional<T&>``","2025-06 (Sofia)","","","`#148131 <https://github.com/llvm/llvm-project/issues/148131>`__",""
"`P2988R12 <https://wg21.link/P2988R12>`__","``std::optional<T&>``","2025-06 (Sofia)","|Complete|","22","`#148131 <https://github.com/llvm/llvm-project/issues/148131>`__",""
"`P3348R4 <https://wg21.link/P3348R4>`__","C++26 should refer to C23 not C17","2025-06 (Sofia)","","","`#148133 <https://github.com/llvm/llvm-project/issues/148133>`__",""
"`P3037R6 <https://wg21.link/P3037R6>`__","``constexpr`` ``std::shared_ptr`` and friends","2025-06 (Sofia)","","","`#148135 <https://github.com/llvm/llvm-project/issues/148135>`__",""
"`P3284R4 <https://wg21.link/P3284R4>`__","``write_env`` and ``unstoppable`` Sender Adaptors","2025-06 (Sofia)","","","`#148136 <https://github.com/llvm/llvm-project/issues/148136>`__",""

1 Paper # Paper Name Meeting Status First released version GitHub issue Notes
122 `P3293R3 <https://wg21.link/P3293R3>`__ Splicing a base class subobject 2025-06 (Sofia) `#148125 <https://github.com/llvm/llvm-project/issues/148125>`__
123 `P3491R3 <https://wg21.link/P3491R3>`__ ``define_static_{string,object,array}`` 2025-06 (Sofia) `#148126 <https://github.com/llvm/llvm-project/issues/148126>`__
124 `P3096R12 <https://wg21.link/P3096R12>`__ Function Parameter Reflection in Reflection for C++26 2025-06 (Sofia) `#148127 <https://github.com/llvm/llvm-project/issues/148127>`__
125 `P2988R12 <https://wg21.link/P2988R12>`__ ``std::optional<T&>`` 2025-06 (Sofia) |Complete| 22 `#148131 <https://github.com/llvm/llvm-project/issues/148131>`__
126 `P3348R4 <https://wg21.link/P3348R4>`__ C++26 should refer to C23 not C17 2025-06 (Sofia) `#148133 <https://github.com/llvm/llvm-project/issues/148133>`__
127 `P3037R6 <https://wg21.link/P3037R6>`__ ``constexpr`` ``std::shared_ptr`` and friends 2025-06 (Sofia) `#148135 <https://github.com/llvm/llvm-project/issues/148135>`__
128 `P3284R4 <https://wg21.link/P3284R4>`__ ``write_env`` and ``unstoppable`` Sender Adaptors 2025-06 (Sofia) `#148136 <https://github.com/llvm/llvm-project/issues/148136>`__

View File

@ -117,8 +117,8 @@ private:
friend class span;
template <class _Tp, size_t _Size>
friend struct array;
template <class _Tp>
friend class optional;
template <class _Tp, class>
friend struct __optional_iterator;
};
template <class _Iter1>

View File

@ -210,6 +210,7 @@ namespace std {
# include <__iterator/wrap_iter.h>
# include <__memory/addressof.h>
# include <__memory/construct_at.h>
# include <__ranges/enable_borrowed_range.h>
# include <__ranges/enable_view.h>
# include <__tuple/sfinae_helpers.h>
# include <__type_traits/add_pointer.h>
@ -239,6 +240,7 @@ namespace std {
# include <__type_traits/is_trivially_relocatable.h>
# include <__type_traits/is_unbounded_array.h>
# include <__type_traits/negation.h>
# include <__type_traits/reference_constructs_from_temporary.h>
# include <__type_traits/remove_const.h>
# include <__type_traits/remove_cv.h>
# include <__type_traits/remove_cvref.h>
@ -409,39 +411,30 @@ struct __optional_storage_base : __optional_destruct_base<_Tp> {
__construct(std::forward<_That>(__opt).__get());
}
}
template <class _Up>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __assign_from_val(_Up&& __val) {
this->__get() = std::forward<_Up>(__val);
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __swap(__optional_storage_base& __rhs) {
using std::swap;
swap(this->__get(), __rhs.__get());
}
};
// optional<T&> is currently required to be ill-formed. However, it may
// be allowed in the future. For this reason, it has already been implemented
// to ensure we can make the change in an ABI-compatible manner.
template <class _Tp>
struct __optional_storage_base<_Tp, true> {
using value_type = _Tp;
using __raw_type _LIBCPP_NODEBUG = remove_reference_t<_Tp>;
__raw_type* __value_;
template <class _Up>
static _LIBCPP_HIDE_FROM_ABI constexpr bool __can_bind_reference() {
using _RawUp = __libcpp_remove_reference_t<_Up>;
using _UpPtr = _RawUp*;
using _RawTp = __libcpp_remove_reference_t<_Tp>;
using _TpPtr = _RawTp*;
using _CheckLValueArg =
integral_constant<bool,
(is_lvalue_reference<_Up>::value && is_convertible<_UpPtr, _TpPtr>::value) ||
is_same<_RawUp, reference_wrapper<_RawTp>>::value ||
is_same<_RawUp, reference_wrapper<__remove_const_t<_RawTp>>>::value >;
return (is_lvalue_reference<_Tp>::value && _CheckLValueArg::value) ||
(is_rvalue_reference<_Tp>::value && !is_lvalue_reference<_Up>::value &&
is_convertible<_UpPtr, _TpPtr>::value);
}
_LIBCPP_HIDE_FROM_ABI constexpr __optional_storage_base() noexcept : __value_(nullptr) {}
template <class _UArg>
_LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_storage_base(in_place_t, _UArg&& __uarg)
: __value_(std::addressof(__uarg)) {
static_assert(__can_bind_reference<_UArg>(),
static_assert(!__reference_constructs_from_temporary_v<_Tp, _UArg>,
"Attempted to construct a reference element in tuple from a "
"possible temporary");
}
@ -457,7 +450,7 @@ 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(__can_bind_reference<_UArg>(),
static_assert(!__reference_constructs_from_temporary_v<_Tp, _UArg>,
"Attempted to construct a reference element in tuple from a "
"possible temporary");
__value_ = std::addressof(__val);
@ -481,6 +474,15 @@ struct __optional_storage_base<_Tp, true> {
__construct(std::forward<_That>(__opt).__get());
}
}
template <class _Up>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __assign_from_val(_Up&& __val) noexcept {
__value_ = std::addressof(__val);
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __swap(__optional_storage_base& __rhs) noexcept {
std::swap(__value_, __rhs.__value_);
}
};
template <class _Tp, bool = is_trivially_copy_constructible<_Tp>::value>
@ -592,6 +594,10 @@ constexpr bool ranges::enable_view<optional<_Tp>> = true;
template <class _Tp>
constexpr range_format format_kind<optional<_Tp>> = range_format::disabled;
template <class _Tp>
constexpr bool ranges::enable_borrowed_range<optional<_Tp&>> = true;
# endif
# if _LIBCPP_STD_VER >= 20
@ -606,19 +612,19 @@ struct __is_std_optional : false_type {};
template <class _Tp>
struct __is_std_optional<optional<_Tp>> : true_type {};
template <class _Tp>
class _LIBCPP_DECLSPEC_EMPTY_BASES optional
: private __optional_move_assign_base<_Tp>,
private __optional_sfinae_ctor_base_t<_Tp>,
private __optional_sfinae_assign_base_t<_Tp> {
using __base _LIBCPP_NODEBUG = __optional_move_assign_base<_Tp>;
template <class _Tp, class = void>
struct __optional_iterator {};
using __pointer _LIBCPP_NODEBUG = std::add_pointer_t<_Tp>;
using __const_pointer _LIBCPP_NODEBUG = std::add_pointer_t<const _Tp>;
template <class _Tp>
struct __optional_iterator<
_Tp,
enable_if_t<!(is_lvalue_reference_v<_Tp> && is_function_v<__libcpp_remove_reference_t<_Tp>>) &&
!(is_lvalue_reference_v<_Tp> && is_array_v<__libcpp_remove_reference_t<_Tp>>)> > {
private:
using __pointer _LIBCPP_NODEBUG = add_pointer_t<remove_reference_t<_Tp>>;
using __const_pointer _LIBCPP_NODEBUG = add_pointer_t<const remove_reference_t<_Tp>>;
public:
using value_type = _Tp;
# if _LIBCPP_STD_VER >= 26
# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
using iterator = __bounded_iter<__wrap_iter<__pointer>>;
@ -627,19 +633,86 @@ public:
using iterator = __wrap_iter<__pointer>;
using const_iterator = __wrap_iter<__const_pointer>;
# endif
// [optional.iterators], iterator support
_LIBCPP_HIDE_FROM_ABI constexpr iterator begin() noexcept {
auto& __derived_self = static_cast<optional<_Tp>&>(*this);
auto __ptr = [&__derived_self]() {
if constexpr (is_lvalue_reference_v<_Tp>) {
return __derived_self.has_value() ? std::addressof(__derived_self.__get()) : nullptr;
}
return std::addressof(__derived_self.__get());
}();
# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
return std::__make_bounded_iter(
__wrap_iter<__pointer>(__ptr),
__wrap_iter<__pointer>(__ptr),
__wrap_iter<__pointer>(__ptr) + (__derived_self.has_value() ? 1 : 0));
# else
return iterator(__ptr);
# endif
}
_LIBCPP_HIDE_FROM_ABI constexpr const_iterator begin() const noexcept {
auto& __derived_self = static_cast<const optional<_Tp>&>(*this);
auto* __ptr = [&__derived_self]() {
if constexpr (is_lvalue_reference_v<_Tp>) {
return __derived_self.has_value() ? std::addressof(__derived_self.__get()) : nullptr;
}
return std::addressof(__derived_self.__get());
}();
# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
return std::__make_bounded_iter(
__wrap_iter<__const_pointer>(__ptr),
__wrap_iter<__const_pointer>(__ptr),
__wrap_iter<__const_pointer>(__ptr) + (__derived_self.has_value() ? 1 : 0));
# else
return const_iterator(__ptr);
# endif
}
_LIBCPP_HIDE_FROM_ABI constexpr iterator end() noexcept {
return begin() + (static_cast<optional<_Tp>&>(*this).has_value() ? 1 : 0);
}
_LIBCPP_HIDE_FROM_ABI constexpr const_iterator end() const noexcept {
return begin() + (static_cast<const optional<_Tp>&>(*this).has_value() ? 1 : 0);
}
# endif
};
template <class _Tp>
class _LIBCPP_DECLSPEC_EMPTY_BASES optional
: private __optional_move_assign_base<_Tp>,
private __optional_sfinae_ctor_base_t<_Tp>,
private __optional_sfinae_assign_base_t<_Tp>,
public __optional_iterator<_Tp> {
using __base _LIBCPP_NODEBUG = __optional_move_assign_base<_Tp>;
public:
using value_type = __libcpp_remove_reference_t<_Tp>;
using __trivially_relocatable _LIBCPP_NODEBUG =
conditional_t<__libcpp_is_trivially_relocatable<_Tp>::value, optional, void>;
private:
// Disable the reference extension using this static assert.
static_assert(!is_same_v<__remove_cvref_t<value_type>, in_place_t>,
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<value_type>, nullopt_t>,
"instantiation of optional with nullopt_t is ill-formed");
static_assert(!is_reference_v<value_type>, "instantiation of optional with a reference type is ill-formed");
static_assert(is_destructible_v<value_type>, "instantiation of optional with a non-destructible type is ill-formed");
static_assert(!is_array_v<value_type>, "instantiation of optional with an array type is ill-formed");
static_assert(!is_same_v<__remove_cvref_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
static_assert(!is_reference_v<_Tp>, "instantiation of optional with a reference type is ill-formed");
# endif
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>;
# endif
// LWG2756: conditionally explicit conversion from _Up
struct _CheckOptionalArgsConstructor {
@ -714,18 +787,15 @@ public:
template <class _InPlaceT,
class... _Args,
enable_if_t<_And<_IsSame<_InPlaceT, in_place_t>, is_constructible<value_type, _Args...>>::value, int> = 0>
enable_if_t<_And<_IsSame<_InPlaceT, in_place_t>, is_constructible<_Tp, _Args...>>::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_InPlaceT, _Args&&... __args)
: __base(in_place, std::forward<_Args>(__args)...) {}
template <class _Up,
class... _Args,
enable_if_t<is_constructible_v<value_type, initializer_list<_Up>&, _Args...>, int> = 0>
template <class _Up, class... _Args, enable_if_t<is_constructible_v<_Tp, initializer_list<_Up>&, _Args...>, int> = 0>
_LIBCPP_HIDE_FROM_ABI constexpr explicit optional(in_place_t, initializer_list<_Up> __il, _Args&&... __args)
: __base(in_place, __il, std::forward<_Args>(__args)...) {}
template <class _Up = value_type,
enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_implicit<_Up>(), int> = 0>
template <class _Up = _Tp, enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_implicit<_Up>(), int> = 0>
_LIBCPP_HIDE_FROM_ABI constexpr optional(_Up&& __v) : __base(in_place, std::forward<_Up>(__v)) {}
template <class _Up = remove_cv_t<_Tp>,
@ -752,6 +822,38 @@ public:
this->__construct_from(std::move(__v));
}
// deleted optional<T&> constructors
# if _LIBCPP_STD_VER >= 26
template <class _Up, class... _Args, enable_if_t<is_constructible_v<_Tp, initializer_list<_Up>&, _Args...>, int> = 0>
requires __libcpp_opt_ref_ctor_deleted<_Up>
explicit optional(in_place_t, initializer_list<_Up>, _Args&&...) = delete;
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;
template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up const&>::template __enable_implicit<_Up>(), int> = 0>
requires __libcpp_opt_ref_ctor_deleted<_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<_Up>
explicit optional(const optional<_Up>&) = delete;
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;
# endif
# if _LIBCPP_STD_VER >= 23
template <class _Tag,
class _Fp,
@ -770,15 +872,15 @@ public:
_LIBCPP_HIDE_FROM_ABI constexpr optional& operator=(optional&&) = default;
// LWG2756
template <class _Up = remove_cv_t<value_type>,
template <class _Up = remove_cv_t<_Tp>,
enable_if_t<_And<_IsNotSame<__remove_cvref_t<_Up>, optional>,
_Or<_IsNotSame<__remove_cvref_t<_Up>, value_type>, _Not<is_scalar<value_type>>>,
is_constructible<value_type, _Up>,
is_assignable<value_type&, _Up>>::value,
_Or<_IsNotSame<__remove_cvref_t<_Up>, _Tp>, _Not<is_scalar<_Tp>>>,
is_constructible<_Tp, _Up>,
is_assignable<_Tp&, _Up>>::value,
int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional& operator=(_Up&& __v) {
if (this->has_value())
this->__get() = std::forward<_Up>(__v);
this->__assign_from_val(std::forward<_Up>(__v));
else
this->__construct(std::forward<_Up>(__v));
return *this;
@ -798,7 +900,7 @@ public:
return *this;
}
template <class... _Args, enable_if_t<is_constructible_v<value_type, _Args...>, int> = 0>
template <class... _Args, enable_if_t<is_constructible_v<_Tp, _Args...>, int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& emplace(_Args&&... __args) {
reset();
this->__construct(std::forward<_Args>(__args)...);
@ -807,7 +909,12 @@ public:
template <class _Up,
class... _Args,
enable_if_t<is_constructible_v<value_type, initializer_list<_Up>&, _Args...>, int> = 0>
enable_if_t<is_constructible_v<_Tp, initializer_list<_Up>&, _Args...>
# if _LIBCPP_STD_VER >= 26
&& !reference_constructs_from_temporary_v<_Tp&, _Up>
# endif
,
int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& emplace(initializer_list<_Up> __il, _Args&&... __args) {
reset();
this->__construct(__il, std::forward<_Args>(__args)...);
@ -815,11 +922,10 @@ public:
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
swap(optional& __opt) noexcept(is_nothrow_move_constructible_v<value_type> && is_nothrow_swappable_v<value_type>) {
swap(optional& __opt) noexcept(is_nothrow_move_constructible_v<_Tp> && is_nothrow_swappable_v<_Tp>) {
if (this->has_value() == __opt.has_value()) {
using std::swap;
if (this->has_value())
swap(this->__get(), __opt.__get());
this->__swap(__opt);
} else {
if (this->has_value()) {
__opt.__construct(std::move(this->__get()));
@ -831,60 +937,32 @@ public:
}
}
# if _LIBCPP_STD_VER >= 26
// [optional.iterators], iterator support
_LIBCPP_HIDE_FROM_ABI constexpr iterator begin() noexcept {
# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
return std::__make_bounded_iter(
std::__wrap_iter<__pointer>(std::addressof(this->__get())),
std::__wrap_iter<__pointer>(std::addressof(this->__get())),
std::__wrap_iter<__pointer>(std::addressof(this->__get()) + (this->has_value() ? 1 : 0)));
# else
return iterator(std::addressof(this->__get()));
# endif
}
_LIBCPP_HIDE_FROM_ABI constexpr const_iterator begin() const noexcept {
# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
return std::__make_bounded_iter(
std::__wrap_iter<__const_pointer>(std::addressof(this->__get())),
std::__wrap_iter<__const_pointer>(std::addressof(this->__get())),
std::__wrap_iter<__const_pointer>(std::addressof(this->__get()) + (this->has_value() ? 1 : 0)));
# else
return const_iterator(std::addressof(this->__get()));
# endif
}
_LIBCPP_HIDE_FROM_ABI constexpr iterator end() noexcept { return begin() + (this->has_value() ? 1 : 0); }
_LIBCPP_HIDE_FROM_ABI constexpr const_iterator end() const noexcept { return begin() + (this->has_value() ? 1 : 0); }
# endif
_LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<value_type const> operator->() const noexcept {
_LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<_Tp const> operator->() const noexcept {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator-> called on a disengaged value");
return std::addressof(this->__get());
}
_LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<value_type> operator->() noexcept {
_LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<_Tp> operator->() noexcept {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator-> called on a disengaged value");
return std::addressof(this->__get());
}
_LIBCPP_HIDE_FROM_ABI constexpr const value_type& operator*() const& noexcept {
_LIBCPP_HIDE_FROM_ABI constexpr const _Tp& operator*() const& noexcept {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
return this->__get();
}
_LIBCPP_HIDE_FROM_ABI constexpr value_type& operator*() & noexcept {
_LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() & noexcept {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
return this->__get();
}
_LIBCPP_HIDE_FROM_ABI constexpr value_type&& operator*() && noexcept {
_LIBCPP_HIDE_FROM_ABI constexpr _Tp&& operator*() && noexcept {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
return std::move(this->__get());
}
_LIBCPP_HIDE_FROM_ABI constexpr const value_type&& operator*() const&& noexcept {
_LIBCPP_HIDE_FROM_ABI constexpr const _Tp&& operator*() const&& noexcept {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
return std::move(this->__get());
}
@ -894,48 +972,66 @@ public:
using __base::__get;
using __base::has_value;
_LIBCPP_HIDE_FROM_ABI constexpr value_type const& value() const& {
_LIBCPP_HIDE_FROM_ABI constexpr _Tp const& value() const& {
if (!this->has_value())
std::__throw_bad_optional_access();
return this->__get();
}
_LIBCPP_HIDE_FROM_ABI constexpr value_type& value() & {
_LIBCPP_HIDE_FROM_ABI constexpr _Tp& value() & {
if (!this->has_value())
std::__throw_bad_optional_access();
return this->__get();
}
_LIBCPP_HIDE_FROM_ABI constexpr value_type&& value() && {
_LIBCPP_HIDE_FROM_ABI constexpr _Tp&& value() && {
if (!this->has_value())
std::__throw_bad_optional_access();
return std::move(this->__get());
}
_LIBCPP_HIDE_FROM_ABI constexpr value_type const&& value() const&& {
_LIBCPP_HIDE_FROM_ABI constexpr _Tp const&& value() const&& {
if (!this->has_value())
std::__throw_bad_optional_access();
return std::move(this->__get());
}
template <class _Up = remove_cv_t<_Tp>>
_LIBCPP_HIDE_FROM_ABI constexpr value_type value_or(_Up&& __v) const& {
static_assert(is_copy_constructible_v<value_type>, "optional<T>::value_or: T must be copy constructible");
static_assert(is_convertible_v<_Up, value_type>, "optional<T>::value_or: U must be convertible to T");
return this->has_value() ? this->__get() : static_cast<value_type>(std::forward<_Up>(__v));
# if _LIBCPP_STD_VER >= 26
requires(!(is_lvalue_reference_v<_Tp> && is_function_v<__libcpp_remove_reference_t<_Tp>>) &&
!(is_lvalue_reference_v<_Tp> && is_array_v<__libcpp_remove_reference_t<_Tp>>))
# endif
_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");
return this->has_value() ? this->__get() : static_cast<_Tp>(std::forward<_Up>(__v));
}
template <class _Up = remove_cv_t<_Tp>>
_LIBCPP_HIDE_FROM_ABI constexpr value_type value_or(_Up&& __v) && {
static_assert(is_move_constructible_v<value_type>, "optional<T>::value_or: T must be move constructible");
static_assert(is_convertible_v<_Up, value_type>, "optional<T>::value_or: U must be convertible to T");
return this->has_value() ? std::move(this->__get()) : static_cast<value_type>(std::forward<_Up>(__v));
# if _LIBCPP_STD_VER >= 26
requires(!is_lvalue_reference_v<_Tp>)
# endif
_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");
return this->has_value() ? std::move(this->__get()) : static_cast<_Tp>(std::forward<_Up>(__v));
}
# if _LIBCPP_STD_VER >= 26
template <class _Up = remove_cv_t<_Tp>>
requires(is_lvalue_reference_v<_Tp> &&
!(is_function_v<__libcpp_remove_reference_t<_Tp>> || is_array_v<__libcpp_remove_reference_t<_Tp>>))
_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");
return this->has_value() ? this->__get() : static_cast<_Tp>(std::forward<_Up>(__v));
}
# endif
# if _LIBCPP_STD_VER >= 23
template <class _Func>
_LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) & {
using _Up = invoke_result_t<_Func, value_type&>;
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");
if (*this)
@ -945,7 +1041,7 @@ public:
template <class _Func>
_LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const& {
using _Up = invoke_result_t<_Func, const value_type&>;
using _Up = invoke_result_t<_Func, const _Tp&>;
static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
"Result of f(value()) must be a specialization of std::optional");
if (*this)
@ -955,7 +1051,7 @@ public:
template <class _Func>
_LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) && {
using _Up = invoke_result_t<_Func, value_type&&>;
using _Up = invoke_result_t<_Func, _Tp&&>;
static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
"Result of f(std::move(value())) must be a specialization of std::optional");
if (*this)
@ -965,7 +1061,7 @@ public:
template <class _Func>
_LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const&& {
using _Up = invoke_result_t<_Func, const value_type&&>;
using _Up = invoke_result_t<_Func, const _Tp&&>;
static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
"Result of f(std::move(value())) must be a specialization of std::optional");
if (*this)
@ -975,7 +1071,7 @@ public:
template <class _Func>
_LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) & {
using _Up = remove_cv_t<invoke_result_t<_Func, value_type&>>;
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");
@ -987,7 +1083,7 @@ public:
template <class _Func>
_LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) const& {
using _Up = remove_cv_t<invoke_result_t<_Func, const value_type&>>;
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");
@ -999,7 +1095,7 @@ public:
template <class _Func>
_LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) && {
using _Up = remove_cv_t<invoke_result_t<_Func, value_type&&>>;
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");
@ -1011,7 +1107,7 @@ public:
template <class _Func>
_LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) const&& {
using _Up = remove_cvref_t<invoke_result_t<_Func, const value_type&&>>;
using _Up = remove_cvref_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");
@ -1023,7 +1119,7 @@ public:
template <invocable _Func>
_LIBCPP_HIDE_FROM_ABI constexpr optional or_else(_Func&& __f) const&
requires is_copy_constructible_v<value_type>
requires is_copy_constructible_v<_Tp>
{
static_assert(is_same_v<remove_cvref_t<invoke_result_t<_Func>>, optional>,
"Result of f() should be the same type as this optional");
@ -1034,7 +1130,7 @@ public:
template <invocable _Func>
_LIBCPP_HIDE_FROM_ABI constexpr optional or_else(_Func&& __f) &&
requires is_move_constructible_v<value_type>
requires is_move_constructible_v<_Tp>
{
static_assert(is_same_v<remove_cvref_t<invoke_result_t<_Func>>, optional>,
"Result of f() should be the same type as this optional");
@ -1336,7 +1432,15 @@ swap(optional<_Tp>& __x, optional<_Tp>& __y) noexcept(noexcept(__x.swap(__y))) {
__x.swap(__y);
}
template <class _Tp>
struct __make_optional_barrier_tag {
explicit __make_optional_barrier_tag() = default;
};
template <
# if _LIBCPP_STD_VER >= 26
__make_optional_barrier_tag = __make_optional_barrier_tag{},
# endif
class _Tp>
_LIBCPP_HIDE_FROM_ABI constexpr optional<decay_t<_Tp>> make_optional(_Tp&& __v) {
return optional<decay_t<_Tp>>(std::forward<_Tp>(__v));
}

View File

@ -187,7 +187,8 @@ __cpp_lib_nonmember_container_access 201411L <array> <deque>
__cpp_lib_not_fn 202306L <functional>
201603L // C++17
__cpp_lib_null_iterators 201304L <iterator>
__cpp_lib_optional 202110L <optional>
__cpp_lib_optional 202506L <optional>
202110L // C++23
202106L // C++20
201606L // C++17
__cpp_lib_optional_range_support 202406L <optional>
@ -594,6 +595,8 @@ __cpp_lib_void_t 201411L <type_traits>
# define __cpp_lib_mdspan 202406L
# undef __cpp_lib_not_fn
# define __cpp_lib_not_fn 202306L
# undef __cpp_lib_optional
# define __cpp_lib_optional 202506L
# define __cpp_lib_optional_range_support 202406L
# undef __cpp_lib_out_ptr
# define __cpp_lib_out_ptr 202311L

View File

@ -13,8 +13,9 @@ export namespace std {
#if _LIBCPP_STD_VER >= 26
// [optional.iterators], iterator support
namespace ranges {
using std::ranges::enable_borrowed_range;
using std::ranges::enable_view;
}
} // namespace ranges
#endif
// [optional.nullopt], no-value state indicator
using std::nullopt;

View File

@ -23,8 +23,7 @@ concept has_iterator_aliases = requires {
static_assert(has_iterator_aliases<std::optional<int>>);
static_assert(has_iterator_aliases<std::optional<const int>>);
// TODO: Uncomment these once P2988R12 is implemented, as they would be testing optional<T&>
// static_assert(!has_iterator_aliases<std::optional<int (&)[]>>);
// static_assert(!has_iterator_aliases<std::optional<void (&)(int, char)>>);
static_assert(has_iterator_aliases<std::optional<int&>>);
static_assert(has_iterator_aliases<std::optional<const int&>>);
static_assert(!has_iterator_aliases<std::optional<int (&)[1]>>);
static_assert(!has_iterator_aliases<std::optional<int (&)()>>);

View File

@ -0,0 +1,28 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// REQUIRES: std-at-least-c++26
// <optional>
// template <class U> T optional<T>::value_or(U&&);
#include <concepts>
#include <optional>
template <typename Opt, typename T>
concept has_value_or = requires(Opt opt, T&& t) {
{ opt.value_or(t) } -> std::same_as<T>;
};
static_assert(has_value_or<std::optional<int>, int>);
static_assert(has_value_or<std::optional<int&>, int&>);
static_assert(has_value_or<std::optional<const int&>, const int&>);
static_assert(!has_value_or<std::optional<int (&)[1]>&&, int (&)[1]>);
static_assert(!has_value_or<std::optional<int (&)()>&&, int (&)()>);

View File

@ -142,8 +142,8 @@
# ifndef __cpp_lib_optional
# error "__cpp_lib_optional should be defined in c++26"
# endif
# if __cpp_lib_optional != 202110L
# error "__cpp_lib_optional should have the value 202110L in c++26"
# if __cpp_lib_optional != 202506L
# error "__cpp_lib_optional should have the value 202506L in c++26"
# endif
# ifndef __cpp_lib_optional_range_support

View File

@ -7509,8 +7509,8 @@
# ifndef __cpp_lib_optional
# error "__cpp_lib_optional should be defined in c++26"
# endif
# if __cpp_lib_optional != 202110L
# error "__cpp_lib_optional should have the value 202110L in c++26"
# if __cpp_lib_optional != 202506L
# error "__cpp_lib_optional should have the value 202506L in c++26"
# endif
# ifndef __cpp_lib_optional_range_support

View File

@ -21,7 +21,8 @@
template <typename T>
constexpr bool test() {
std::optional<T> opt{T{}};
std::remove_reference_t<T> t = std::remove_reference_t<T>{};
std::optional<T> opt{t};
{ // begin() is marked noexcept
static_assert(noexcept(opt.begin()));
@ -53,6 +54,10 @@ constexpr bool tests() {
assert(test<char>());
assert(test<const int>());
assert(test<const char>());
assert(test<int&>());
assert(test<char&>());
assert(test<const int&>());
assert(test<const char&>());
return true;
}

View File

@ -0,0 +1,34 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// REQUIRES: std-at-least-c++26
// <optional>
// template <class T> class optional<T&>::iterator;
// template <class T> class optional<T&>::const_iterator;
// template <class T>
// constexpr bool ranges::enable_borrowed_range<optional<T&>> = true;
#include <cassert>
#include <optional>
#include <ranges>
template <typename T>
void borrowed_range() {
static_assert(std::ranges::enable_borrowed_range<std::optional<T&>>);
static_assert(std::ranges::range<std::optional<T&>> == std::ranges::borrowed_range<std::optional<T&>>);
}
void test_borrowed_range() {
borrowed_range<int>();
borrowed_range<const int>();
borrowed_range<int[]>();
borrowed_range<int[10]>();
borrowed_range<int()>();
}

View File

@ -17,6 +17,7 @@
#include <iterator>
#include <optional>
#include <ranges>
#include <type_traits>
#include <utility>
template <typename T>
@ -41,7 +42,8 @@ constexpr bool test() {
assert(it2 == std::as_const(disengaged).end());
}
std::optional<T> engaged{T{}};
std::remove_reference_t<T> t = std::remove_reference_t<T>{};
std::optional<T> engaged{t};
{ // end() != begin() if the optional is engaged
auto it = engaged.end();
@ -62,6 +64,10 @@ constexpr bool tests() {
assert(test<char>());
assert(test<const int>());
assert(test<const char>());
assert(test<int&>());
assert(test<char&>());
assert(test<const int&>());
assert(test<const char&>());
return true;
}

View File

@ -14,15 +14,23 @@
// template <class T> class optional::const_iterator;
#include <cassert>
#include <iterator>
#include <optional>
#include <ranges>
#include <type_traits>
#include <utility>
template <typename T, T __val>
template <typename T>
constexpr bool test_range_concept() {
return std::ranges::range<std::optional<T>>;
}
template <typename T, std::remove_reference_t<T> __val>
constexpr bool test() {
std::optional<T> opt{__val};
std::remove_reference_t<T> v{__val};
std::optional<T> opt{v};
{
assert(test_range_concept<T>());
}
{ // Dereferencing an iterator of an engaged optional will return the same value that the optional holds.
auto it = opt.begin();
@ -41,13 +49,14 @@ constexpr bool test() {
assert(std::random_access_iterator<decltype(it2)>);
}
{ // const_iterator::value_type == std::remove_cv_t<T>, const_iterator::reference == const T&, iterator::value_type = std::remove_cv_t<T>, iterator::reference == T&
{ // const_iterator::value_type == std::remove_cvref_t<T>, const_iterator::reference == const T&, iterator::value_type = std::remove_cvref_t<T>, iterator::reference == T&
// std::remove_cv_t is impossible for optional<T&>
auto it = opt.begin();
auto it2 = std::as_const(opt).begin();
assert((std::is_same_v<typename decltype(it)::value_type, std::remove_cv_t<T>>));
assert((std::is_same_v<typename decltype(it)::reference, T&>));
assert((std::is_same_v<typename decltype(it2)::value_type, std::remove_cv_t<T>>));
assert((std::is_same_v<typename decltype(it2)::reference, const T&>));
assert((std::is_same_v<typename decltype(it)::value_type, std::remove_cvref_t<T>>));
assert((std::is_same_v<typename decltype(it)::reference, std::remove_reference_t<T>&>));
assert((std::is_same_v<typename decltype(it2)::value_type, std::remove_cvref_t<T>>));
assert((std::is_same_v<typename decltype(it2)::reference, const std::remove_reference_t<T>&>));
}
{ // std::ranges::size for an engaged optional<T> == 1, disengaged optional<T> == 0
@ -68,13 +77,13 @@ constexpr bool test() {
// An optional with value that is reset will have a begin() == end(), then when it is reassigned a value,
// begin() != end(), and *begin() will contain the new value.
{
std::optional<T> val{__val};
std::optional<T> val{v};
assert(val.begin() != val.end());
val.reset();
assert(val.begin() == val.end());
val.emplace(__val);
val.emplace(v);
assert(val.begin() != val.end());
assert(*(val.begin()) == __val);
assert(*(val.begin()) == v);
}
return true;
@ -86,6 +95,15 @@ constexpr bool tests() {
assert((test<bool, true>()));
assert((test<const int, 2>()));
assert((test<const char, 'b'>()));
assert((test<int&, 1>()));
assert((test<char&, 'a'>()));
assert((test<bool&, true>()));
assert((test<const int&, 2>()));
assert((test<const char&, 'b'>()));
assert(!test_range_concept<int (&)()>());
assert(!test_range_concept<int (&)[]>());
assert(!test_range_concept<int (&)[42]>());
return true;
}

View File

@ -16,6 +16,7 @@
// template<class F> constexpr auto and_then(F&&) const&&;
#include <cassert>
#include <concepts>
#include <optional>
#include "test_macros.h"
@ -257,8 +258,94 @@ constexpr bool test() {
return true;
}
#if TEST_STD_VER >= 26
constexpr bool test_ref() {
// Test & overload
{
// Without & qualifier on F's operator()
{
int j = 42;
std::optional<int&> i{j};
std::same_as<std::optional<int>> decltype(auto) r = i.and_then(LVal{});
assert(r == 1);
assert(i.and_then(NOLVal{}) == std::nullopt);
}
//With & qualifier on F's operator()
{
int j = 42;
std::optional<int&> i{j};
RefQual l{};
NORefQual nl{};
std::same_as<std::optional<int>> decltype(auto) r = i.and_then(l);
assert(r == 1);
assert(i.and_then(nl) == std::nullopt);
}
}
// Test const& overload
{
// Without & qualifier on F's operator()
{
int j = 42;
std::optional<const int&> i{j};
std::same_as<std::optional<int>> decltype(auto) r = i.and_then(CLVal{});
assert(r == 1);
assert(i.and_then(NOCLVal{}) == std::nullopt);
}
//With & qualifier on F's operator()
{
int j = 42;
const std::optional<int&> i{j};
const CRefQual l{};
const NOCRefQual nl{};
std::same_as<std::optional<int>> decltype(auto) r = i.and_then(l);
assert(r == 1);
assert(i.and_then(nl) == std::nullopt);
}
}
// Test && overload
{
//With & qualifier on F's operator()
{
int j = 42;
std::optional<int&> i{j};
std::same_as<std::optional<int>> decltype(auto) r = i.and_then(RVRefQual{});
assert(r == 1);
assert(i.and_then(NORVRefQual{}) == std::nullopt);
}
}
// Test const&& overload
{
//With & qualifier on F's operator()
{
int j = 42;
const std::optional<int&> i{j};
const RVCRefQual l{};
const NORVCRefQual nl{};
std::same_as<std::optional<int>> decltype(auto) r = i.and_then(std::move(l));
assert(r == 1);
assert(i.and_then(std::move(nl)) == std::nullopt);
}
}
return true;
}
#endif
int main(int, char**) {
test();
static_assert(test());
#if TEST_STD_VER >= 26
test_ref();
static_assert(test_ref());
#endif
return 0;
}

View File

@ -62,6 +62,32 @@ constexpr bool test() {
return std::optional<MoveOnly>{};
});
}
#if TEST_STD_VER >= 26
{
int i = 2;
std::optional<int&> opt;
assert(opt.or_else([&] { return std::optional<int&>{i}; }) == i);
int j = 3;
opt = j;
opt.or_else([] {
assert(false);
return std::optional<int&>{};
});
assert(opt == j);
}
{
int i = 2;
std::optional<int&> opt;
assert(std::move(opt).or_else([&] { return std::optional<int&>{i}; }) == i);
int j = 3;
opt = j;
std::move(opt).or_else([] {
assert(false);
return std::optional<int&>{};
});
assert(opt == j);
}
#endif
return true;
}

View File

@ -17,62 +17,64 @@
#include "test_macros.h"
#include <cassert>
#include <concepts>
#include <optional>
#include <type_traits>
#include <utility>
struct LVal {
constexpr int operator()(int&) { return 1; }
int operator()(const int&) = delete;
int operator()(int&&) = delete;
int operator()(const int&) = delete;
int operator()(int&&) = delete;
int operator()(const int&&) = delete;
};
struct CLVal {
int operator()(int&) = delete;
constexpr int operator()(const int&) { return 1; }
int operator()(int&&) = delete;
int operator()(int&&) = delete;
int operator()(const int&&) = delete;
};
struct RVal {
int operator()(int&) = delete;
int operator()(int&) = delete;
int operator()(const int&) = delete;
constexpr int operator()(int&&) { return 1; }
int operator()(const int&&) = delete;
};
struct CRVal {
int operator()(int&) = delete;
int operator()(int&) = delete;
int operator()(const int&) = delete;
int operator()(int&&) = delete;
int operator()(int&&) = delete;
constexpr int operator()(const int&&) { return 1; }
};
struct RefQual {
constexpr int operator()(int) & { return 1; }
int operator()(int) const& = delete;
int operator()(int) && = delete;
int operator()(int) const& = delete;
int operator()(int) && = delete;
int operator()(int) const&& = delete;
};
struct CRefQual {
int operator()(int) & = delete;
constexpr int operator()(int) const& { return 1; }
int operator()(int) && = delete;
int operator()(int) && = delete;
int operator()(int) const&& = delete;
};
struct RVRefQual {
int operator()(int) & = delete;
int operator()(int) & = delete;
int operator()(int) const& = delete;
constexpr int operator()(int) && { return 1; }
int operator()(int) const&& = delete;
};
struct RVCRefQual {
int operator()(int) & = delete;
int operator()(int) & = delete;
int operator()(int) const& = delete;
int operator()(int) && = delete;
int operator()(int) && = delete;
constexpr int operator()(int) const&& { return 1; }
};
@ -83,7 +85,7 @@ struct NoCopy {
};
struct NoMove {
NoMove() = default;
NoMove() = default;
NoMove(NoMove&&) = delete;
NoMove operator()(const NoCopy&&) { return NoMove{}; }
};
@ -200,8 +202,111 @@ constexpr bool test() {
return true;
}
#if TEST_STD_VER >= 26
constexpr bool test_ref() {
{
std::optional<int&> opt1;
std::same_as<std::optional<int>> decltype(auto) opt1r = opt1.transform([](int i) { return i + 2; });
assert(!opt1);
assert(!opt1r);
}
{
int i = 42;
std::optional<int&> opt{i};
std::same_as<std::optional<int>> decltype(auto) o2 = opt.transform([](int j) { return j + 2; });
assert(*o2 == 44);
}
// Test & overload
{
// Without & qualifier on F's operator()
{
int i = 42;
std::optional<int&> opt{i};
std::same_as<std::optional<int>> decltype(auto) o3 = opt.transform(LVal{});
assert(*o3 == 1);
}
//With & qualifier on F's operator()
{
int i = 42;
std::optional<int&> opt{i};
RefQual l{};
std::same_as<std::optional<int>> decltype(auto) o3 = opt.transform(l);
assert(*o3 == 1);
}
}
// const& overload
{
// Without & qualifier on F's operator()
{
int i = 42;
std::optional<const int&> opt{i};
std::same_as<std::optional<int>> decltype(auto) o3 = std::as_const(opt).transform(CLVal{});
assert(*o3 == 1);
}
//With & qualifier on F's operator()
{
int i = 42;
const std::optional<int&> opt{i};
const CRefQual l{};
std::same_as<std::optional<int>> decltype(auto) o3 = opt.transform(l);
assert(*o3 == 1);
}
}
// Test && overload
{
// Without & qualifier on F's operator()
{
int i = 42;
std::optional<int> opt{i};
std::same_as<std::optional<int>> decltype(auto) o3 = std::move(opt).transform(RVal{});
assert(*o3 == 1);
}
//With & qualifier on F's operator()
{
int i = 42;
std::optional<int&> opt{i};
std::same_as<std::optional<int>> decltype(auto) o3 = std::move(opt).transform(RVRefQual{});
assert(*o3 == 1);
}
}
// const&& overload
{
//With & qualifier on F's operator()
{
int i = 42;
std::optional<int&> opt{i};
const RVCRefQual rvc{};
std::same_as<std::optional<int>> decltype(auto) o3 = opt.transform(std::move(rvc));
assert(*o3 == 1);
}
}
{
std::optional<int&> o6 = std::nullopt;
auto o6r = o6.transform([](int) { return 42; });
assert(!o6r);
}
return true;
}
#endif
int main(int, char**) {
test();
static_assert(test());
#if TEST_STD_VER >= 26
test_ref();
static_assert(test_ref());
#endif
return 0;
}

View File

@ -250,6 +250,57 @@ constexpr T pr38638(T v)
return *o + 2;
}
#if TEST_STD_VER >= 26
template <typename T, std::remove_reference_t<T> _Val>
constexpr void test_with_ref() {
T t{_Val};
{ // to empty
optional<T&> opt;
opt = t;
assert(static_cast<bool>(opt) == true);
assert(*opt == t);
}
{ // to existing
optional<T&> opt{t};
opt = t;
assert(static_cast<bool>(opt) == true);
assert(*opt == t);
}
{ // test default argument
optional<T&> opt;
opt = {t};
assert(static_cast<bool>(opt) == true);
assert(*opt == t);
}
{ // test default argument
optional<T&> opt{t};
opt = {};
assert(static_cast<bool>(opt) == false);
}
// test two objects, make sure that the optional only changes what it holds a reference to
{
T t2{_Val};
optional<T&> opt{t};
opt = t2;
assert(std::addressof(*opt) != std::addressof(t));
assert(std::addressof(*opt) == std::addressof(t2));
}
// test that reassigning the reference for an optional<T&> doesn't affect the objet it's holding a reference to
{
int i = -1;
int j = 2;
optional<int&> opt{i};
opt = j;
assert(i == -1);
assert(std::addressof(*opt) != std::addressof(i));
assert(std::addressof(*opt) == std::addressof(j));
assert(*opt == 2);
}
}
#endif
int main(int, char**)
{
@ -281,5 +332,8 @@ int main(int, char**)
static_assert(pr38638(3) == 5, "");
return 0;
#if TEST_STD_VER >= 26
test_with_ref<int, 3>();
#endif
return 0;
}

View File

@ -221,6 +221,24 @@ TEST_CONSTEXPR_CXX20 bool test_empty_emplace() {
return true;
}
#if TEST_STD_VER >= 26
template <class T, std::remove_reference_t<T> _Val>
constexpr bool test_ref() {
using Opt = std::optional<T&>;
T t{_Val};
{
Opt opt;
auto& v = opt.emplace(t);
static_assert(std::is_same_v<T&, decltype(v)>);
assert(static_cast<bool>(opt) == true);
assert(*opt == t);
assert(&v == &*opt);
assert(&t == &*opt);
}
return true;
}
#endif
int main(int, char**)
{
{
@ -291,6 +309,11 @@ int main(int, char**)
}
}
#endif
return 0;
#if TEST_STD_VER >= 26
static_assert(test_ref<int, 1>());
static_assert(test_ref<double, 15.0>());
assert((test_ref<int, 1>()));
assert((test_ref<double, 15.0>()));
#endif
return 0;
}

View File

@ -23,18 +23,26 @@ struct NonDestructible { ~NonDestructible() = delete; };
int main(int, char**)
{
{
std::optional<char &> o1; // expected-error-re@optional:* {{static assertion failed{{.*}}instantiation of optional with a reference type is ill-formed}}
std::optional<NonDestructible> o2; // expected-error-re@optional:* {{static assertion failed{{.*}}instantiation of optional with a non-destructible type is ill-formed}}
std::optional<char[20]> o3; // expected-error-re@optional:* {{static assertion failed{{.*}}instantiation of optional with an array type is ill-formed}}
}
{
#if TEST_STD_VER >= 26
std::optional<int&&>
opt2; // expected-error-re@optional:* {{static assertion failed{{.*}}instantiation of optional with an rvalue reference type is ill-formed}}
#else
std::optional<char&>
o1; // expected-error-re@optional:* {{static assertion failed{{.*}}instantiation of optional with a reference type is ill-formed}}
#endif
std::optional<NonDestructible>
o2; // expected-error-re@optional:* {{static assertion failed{{.*}}instantiation of optional with a non-destructible type is ill-formed}}
std::optional<char[20]>
o3; // expected-error-re@optional:* {{static assertion failed{{.*}}instantiation of optional with an array type is ill-formed}}
}
{
{
std::optional< std::in_place_t> o1; // expected-error-re@optional:* {{static assertion failed{{.*}}instantiation of optional with in_place_t is ill-formed}}
std::optional<const std::in_place_t> o2; // expected-error-re@optional:* {{static assertion failed{{.*}}instantiation of optional with in_place_t is ill-formed}}
std::optional< volatile std::in_place_t> o3; // expected-error-re@optional:* {{static assertion failed{{.*}}instantiation of optional with in_place_t is ill-formed}}
std::optional<const volatile std::in_place_t> o4; // expected-error-re@optional:* {{static assertion failed{{.*}}instantiation of optional with in_place_t is ill-formed}}
}
}
{
std::optional< std::nullopt_t> o1; // expected-error-re@optional:* {{static assertion failed{{.*}}instantiation of optional with nullopt_t is ill-formed}}

View File

@ -78,71 +78,71 @@ void test_ref(InitArgs&&... args)
assert(&(*lhs) == &(*rhs));
}
void test_reference_extension()
{
#if defined(_LIBCPP_VERSION) && 0 // FIXME these extensions are currently disabled.
using T = TestTypes::TestType;
T::reset();
{
T t;
T::reset_constructors();
test_ref<T&>();
test_ref<T&>(t);
assert(T::alive == 1);
assert(T::constructed == 0);
assert(T::assigned == 0);
assert(T::destroyed == 0);
}
assert(T::destroyed == 1);
assert(T::alive == 0);
{
T t;
const T& ct = t;
T::reset_constructors();
test_ref<T const&>();
test_ref<T const&>(t);
test_ref<T const&>(ct);
assert(T::alive == 1);
assert(T::constructed == 0);
assert(T::assigned == 0);
assert(T::destroyed == 0);
}
assert(T::alive == 0);
assert(T::destroyed == 1);
{
T t;
T::reset_constructors();
test_ref<T&&>();
test_ref<T&&>(std::move(t));
assert(T::alive == 1);
assert(T::constructed == 0);
assert(T::assigned == 0);
assert(T::destroyed == 0);
}
assert(T::alive == 0);
assert(T::destroyed == 1);
{
T t;
const T& ct = t;
T::reset_constructors();
test_ref<T const&&>();
test_ref<T const&&>(std::move(t));
test_ref<T const&&>(std::move(ct));
assert(T::alive == 1);
assert(T::constructed == 0);
assert(T::assigned == 0);
assert(T::destroyed == 0);
}
assert(T::alive == 0);
assert(T::destroyed == 1);
{
static_assert(!std::is_copy_constructible<std::optional<T&&>>::value, "");
static_assert(!std::is_copy_constructible<std::optional<T const&&>>::value, "");
}
void test_reference_extension() {
#if TEST_STD_VER >= 26
using T = TestTypes::TestType;
T::reset();
{
T t;
T::reset_constructors();
test_ref<T&>();
test_ref<T&>(t);
assert(T::alive == 1);
assert(T::constructed == 0);
assert(T::assigned == 0);
assert(T::destroyed == 0);
}
assert(T::destroyed == 1);
assert(T::alive == 0);
{
T t;
const T& ct = t;
T::reset_constructors();
test_ref<T const&>();
test_ref<T const&>(t);
test_ref<T const&>(ct);
assert(T::alive == 1);
assert(T::constructed == 0);
assert(T::assigned == 0);
assert(T::destroyed == 0);
}
assert(T::alive == 0);
assert(T::destroyed == 1);
# if 0 // FIXME: optional<T&&> is not allowed.
{
T t;
T::reset_constructors();
test_ref<T&&>();
test_ref<T&&>(std::move(t));
assert(T::alive == 1);
assert(T::constructed == 0);
assert(T::assigned == 0);
assert(T::destroyed == 0);
}
assert(T::alive == 0);
assert(T::destroyed == 1);
{
T t;
const T& ct = t;
T::reset_constructors();
test_ref<T const&&>();
test_ref<T const&&>(std::move(t));
test_ref<T const&&>(std::move(ct));
assert(T::alive == 1);
assert(T::constructed == 0);
assert(T::assigned == 0);
assert(T::destroyed == 0);
}
assert(T::alive == 0);
assert(T::destroyed == 1);
{
static_assert(!std::is_copy_constructible_v<std::optional<T&&>>);
static_assert(!std::is_copy_constructible_v<std::optional<T const&&>>);
}
# endif
#endif
}
int main(int, char**)
{
test<int>();

View File

@ -0,0 +1,35 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// REQUIRES: std-at-least-c++26
// optional
#include <optional>
#include <utility>
struct X {
int i;
X(int j) : i(j) {}
};
int main(int, char**) {
const std::optional<int> _co(1);
std::optional<int> _o(1);
// expected-error-re@*:* 8 {{call to deleted constructor of 'std::optional<{{.*}}>'}}
std::optional<const int&> o1{1}; // optional(U&&)
std::optional<const int&> o2{std::optional<int>(1)}; // optional(optional<U>&&)
std::optional<const int&> o3{_co}; // optional(const optional<U>&)
std::optional<const int&> o4{_o}; // optional(optional<U>&)
std::optional<const X&> o5{1}; // optional(U&&)
std::optional<const X&> o6{std::optional<int>(1)}; // optional(optional<U>&&)
std::optional<const X&> o7{_co}; // optional(const optional<U>&)
std::optional<const X&> o8{_o}; // optional(optional<U>&)
}

View File

@ -0,0 +1,75 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// REQUIRES: std-at-least-c++26
// <optional>
#include <cassert>
#include <optional>
#include <type_traits>
#include <utility>
template <typename RefType, std::remove_reference_t<RefType> _Val>
constexpr bool test() {
std::remove_reference_t<RefType> item{_Val};
std::optional<RefType> opt{item};
{
assert(*opt == item);
assert(&(*opt) == &item);
}
{
assert(*std::as_const(opt) == item);
assert(&(*std::as_const(opt)) == &item);
}
return true;
}
template <typename T>
constexpr T foo(T val) {
return val;
}
template <typename T, T _Val>
constexpr bool fn_ref_test() {
std::optional<T (&)(T)> opt{foo<T>};
assert(opt.has_value());
assert((*opt)(_Val) == _Val);
return true;
}
template <typename T, T _Val>
constexpr bool array_ref_test() {
T arr[5]{};
std::optional<T(&)[5]> opt{arr};
assert(opt.has_value());
(*opt)[0] = _Val;
assert((*opt)[0] == _Val);
assert(arr[0] == _Val);
return true;
}
constexpr bool tests() {
assert((test<int&, 1>()));
assert((test<double&, 1.0>()));
assert((fn_ref_test<int, 1>()));
assert((array_ref_test<int, 1>()));
assert((fn_ref_test<double, 1.0>()));
assert((array_ref_test<double, 1.0>()));
return true;
}
int main(int, char**) {
static_assert(tests());
tests();
}

View File

@ -11,9 +11,9 @@
// ~optional();
#include <cassert>
#include <optional>
#include <type_traits>
#include <cassert>
#include "test_macros.h"
@ -64,6 +64,24 @@ int main(int, char**)
}
assert(X::dtor_called == true);
}
#if TEST_STD_VER >= 26
{
typedef X& T;
static_assert(std::is_trivially_destructible_v<T>);
static_assert(std::is_trivially_destructible_v<optional<T>>);
}
X::dtor_called = false;
X x;
{
optional<X&> opt{x};
assert(X::dtor_called == false);
}
assert(X::dtor_called == false);
return 0;
{
static_assert(std::is_trivially_destructible_v<X (&)()>);
static_assert(std::is_trivially_destructible_v<optional<X (&)()>>);
}
#endif
return 0;
}

View File

@ -69,5 +69,16 @@ int main(int, char**)
X::dtor_called = false;
}
return 0;
#if TEST_STD_VER >= 26
{
X x{};
optional<X&> opt(x);
X::dtor_called = false;
opt.reset();
assert(X::dtor_called == false);
assert(static_cast<bool>(opt) == false);
}
#endif
return 0;
}

View File

@ -50,7 +50,19 @@ int main(int, char**)
optional<X> opt(X{});
assert((*opt).test() == 4);
}
#if TEST_STD_VER >= 26
{
X x{};
optional<X&> opt(x);
ASSERT_SAME_TYPE(decltype(*opt), X&);
ASSERT_NOEXCEPT(*opt);
}
{
X x{};
optional<X&> opt(x);
assert((*opt).test() == 4);
}
#endif
static_assert(test() == 7, "");
return 0;
}

View File

@ -43,6 +43,25 @@ int main(int, char**)
constexpr optional<X> opt(X{});
static_assert((*opt).test() == 3, "");
}
#if TEST_STD_VER >= 26
{
X x{};
const optional<X&> opt{x};
ASSERT_SAME_TYPE(decltype(*opt), X&);
ASSERT_NOEXCEPT(*opt);
}
{
X x{};
const optional<const X&> opt{x};
ASSERT_SAME_TYPE(decltype(*opt), const X&);
ASSERT_NOEXCEPT(*opt);
}
{
static constexpr X x{};
constexpr optional<const X&> opt(x);
static_assert((*opt).test() == 3);
}
#endif
{
constexpr optional<Y> opt(Y{});
assert((*opt).test() == 2);

View File

@ -33,6 +33,13 @@ int main(int, char**)
constexpr optional<int> opt(0);
static_assert(opt.has_value(), "");
}
#if TEST_STD_VER >= 26
{
static constexpr int i = 0;
constexpr optional<const int&> opt{i};
static_assert(opt.has_value());
}
#endif
return 0;
return 0;
}

View File

@ -19,9 +19,9 @@
using std::optional;
struct X
{
int test() noexcept {return 3;}
struct X {
int test() noexcept { return 3; }
int test() const noexcept { return 3; }
};
struct Y
@ -47,6 +47,30 @@ int main(int, char**)
optional<X> opt(X{});
assert(opt->test() == 3);
}
#if TEST_STD_VER >= 26
{
X x{};
std::optional<X&> opt(x);
ASSERT_SAME_TYPE(decltype(opt.operator->()), X*);
ASSERT_NOEXCEPT(opt.operator->());
}
{
X x{};
std::optional<const X&> opt(x);
ASSERT_SAME_TYPE(decltype(opt.operator->()), const X*);
ASSERT_NOEXCEPT(opt.operator->());
}
{
X x{};
optional<X&> opt{x};
assert(opt->test() == 3);
}
{
X x{};
optional<const X&> opt{x};
assert(opt->test() == 3);
}
#endif
{
static_assert(test() == 3, "");
}

View File

@ -54,6 +54,25 @@ int main(int, char**)
constexpr optional<Z> opt(Z{});
static_assert(opt->test() == 1, "");
}
#if TEST_STD_VER >= 26
{
X x{};
const std::optional<X&> opt(x);
ASSERT_SAME_TYPE(decltype(opt.operator->()), X*);
ASSERT_NOEXCEPT(opt.operator->());
}
{
X x{};
const std::optional<const X&> opt(x);
ASSERT_SAME_TYPE(decltype(opt.operator->()), const X*);
ASSERT_NOEXCEPT(opt.operator->());
}
{
static constexpr Z z{};
constexpr optional<const Z&> opt(z);
static_assert(opt->test() == 1);
}
#endif
return 0;
}

View File

@ -56,6 +56,14 @@ int main(int, char**)
opt.emplace();
assert(opt.value().test() == 4);
}
#if TEST_STD_VER >= 26
{
X x;
optional<X&> opt{x};
ASSERT_NOT_NOEXCEPT(opt.value());
ASSERT_SAME_TYPE(decltype(opt.value()), X&);
}
#endif
#ifndef TEST_HAS_NO_EXCEPTIONS
{
optional<X> opt;

View File

@ -80,6 +80,14 @@ constexpr int test()
assert((std::move(opt).value_or({2, 3}) == Z{2, 3}));
assert(!opt);
}
#if TEST_STD_VER >= 26
{
int y = 2;
optional<int&> opt;
assert(std::move(opt).value_or(y) == 2);
assert(!opt);
}
#endif
return 0;
}

View File

@ -79,6 +79,12 @@ int main(int, char**)
const optional<X> opt;
assert(opt.value_or({Y(3)}) == 4);
}
return 0;
#if TEST_STD_VER >= 26
{
X y{3};
const optional<X&> opt;
assert(opt.value_or(y) == 3);
}
#endif
return 0;
}

View File

@ -13,9 +13,10 @@
// noexcept(is_nothrow_move_constructible<T>::value &&
// is_nothrow_swappable<T>::value)
#include <cassert>
#include <memory>
#include <optional>
#include <type_traits>
#include <cassert>
#include "test_macros.h"
#include "archetypes.h"
@ -127,6 +128,74 @@ TEST_CONSTEXPR_CXX20 bool check_swap()
return true;
}
#if TEST_STD_VER >= 26
template <typename T>
constexpr bool check_swap_ref() {
{
optional<T&> opt1;
optional<T&> opt2;
static_assert(noexcept(opt1.swap(opt2)) == true);
assert(static_cast<bool>(opt1) == false);
assert(static_cast<bool>(opt2) == false);
opt1.swap(opt2);
assert(static_cast<bool>(opt1) == false);
assert(static_cast<bool>(opt2) == false);
}
{
T one{1};
optional<T&> opt1(one);
optional<T&> opt2;
static_assert(noexcept(opt1.swap(opt2)) == true);
assert(static_cast<bool>(opt1) == true);
assert(std::addressof(*opt1) == std::addressof(one));
assert(static_cast<bool>(opt2) == false);
opt1.swap(opt2);
assert(static_cast<bool>(opt1) == false);
assert(static_cast<bool>(opt2) == true);
assert(std::addressof(*opt2) == std::addressof(one));
}
{
T two{2};
optional<T&> opt1;
optional<T&> opt2(two);
static_assert(noexcept(opt1.swap(opt2)) == true);
assert(static_cast<bool>(opt1) == false);
assert(static_cast<bool>(opt2) == true);
assert(std::addressof(*opt2) == std::addressof(two));
opt1.swap(opt2);
assert(static_cast<bool>(opt1) == true);
assert(std::addressof(*opt1) == std::addressof(two));
assert(static_cast<bool>(opt2) == false);
}
{
T one{1};
T two{2};
optional<T&> opt1(one);
optional<T&> opt2(two);
static_assert(noexcept(opt1.swap(opt2)) == true);
assert(static_cast<bool>(opt1) == true);
assert(*opt1 == 1);
assert(std::addressof(*opt1) == std::addressof(one));
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 2);
assert(std::addressof(*opt2) == std::addressof(two));
opt1.swap(opt2);
assert(static_cast<bool>(opt1) == true);
assert(*opt1 == 2);
assert(std::addressof(*opt1) == std::addressof(two));
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 1);
assert(std::addressof(*opt2) == std::addressof(one));
}
return true;
}
#endif
int main(int, char**)
{
check_swap<int>();
@ -134,6 +203,12 @@ int main(int, char**)
#if TEST_STD_VER > 17
static_assert(check_swap<int>());
static_assert(check_swap<W>());
#endif
#if TEST_STD_VER >= 26
static_assert(check_swap_ref<int>());
static_assert(check_swap_ref<W>());
check_swap_ref<int>();
check_swap_ref<W>();
#endif
{
optional<X> opt1;

View File

@ -13,6 +13,8 @@
#include <optional>
#include "test_macros.h"
using std::optional;
struct X
@ -25,9 +27,13 @@ int main(int, char**)
{
using std::optional;
{
// expected-error-re@optional:* 2 {{static assertion failed{{.*}}instantiation of optional with a reference type is ill-formed}}
optional<int&> opt1;
optional<int&&> opt2;
#if TEST_STD_VER >= 26
// expected-error-re@optional:* {{static assertion failed{{.*}}instantiation of optional with an rvalue reference type is ill-formed}}
#else
// expected-error-re@optional:* 2 {{static assertion failed{{.*}}instantiation of optional with a reference type is ill-formed}}
#endif
optional<int&> opt1;
optional<int&&> opt2;
}
{
// expected-error-re@optional:* {{static assertion failed{{.*}}instantiation of optional with a non-destructible type is ill-formed}}

View File

@ -36,6 +36,11 @@ int main(int, char**)
test<optional<const int>, const int>();
test<optional<double>, double>();
test<optional<const double>, const double>();
return 0;
#if TEST_STD_VER >= 26
test<optional<int&>, int>();
test<optional<const int&>, const int>();
test<optional<double&>, double>();
test<optional<const double&>, const double>();
#endif
return 0;
}

View File

@ -13,10 +13,10 @@
// template <class T>
// constexpr optional<decay_t<T>> make_optional(T&& v);
#include <cassert>
#include <memory>
#include <optional>
#include <string>
#include <memory>
#include <cassert>
#include "test_macros.h"

View File

@ -15,13 +15,30 @@
// GCC crashes on this file, see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120577
// XFAIL: gcc-15
#include <cassert>
#include <memory>
#include <optional>
#include <string>
#include <memory>
#include <cassert>
#include <string_view>
#include "test_macros.h"
template <typename T>
constexpr bool test_ref() {
T i{0};
auto opt = std::make_optional<T&>(i);
#if TEST_STD_VER < 26
assert((std::is_same_v<decltype(opt), std::optional<T>>));
#else
assert((std::is_same_v<decltype(opt), std::optional<T&>>));
#endif
assert(*opt == 0);
return true;
}
int main(int, char**)
{
{
@ -43,6 +60,12 @@ int main(int, char**)
auto opt = std::make_optional<std::string>(4u, 'X');
assert(*opt == "XXXX");
}
using namespace std::string_view_literals;
return 0;
static_assert(test_ref<int>());
assert((test_ref<int>()));
static_assert(test_ref<double>());
assert((test_ref<double>()));
return 0;
}

View File

@ -12,9 +12,10 @@
// template <class T> void swap(optional<T>& x, optional<T>& y)
// noexcept(noexcept(x.swap(y)));
#include <cassert>
#include <memory>
#include <optional>
#include <type_traits>
#include <cassert>
#include "test_macros.h"
#include "archetypes.h"
@ -109,9 +110,82 @@ void test_swap_sfinae() {
}
}
#if TEST_STD_VER >= 26
template <typename T>
constexpr bool test_swap_ref() {
{
optional<T&> opt1;
optional<T&> opt2;
static_assert(noexcept(swap(opt1, opt2)) == true);
assert(static_cast<bool>(opt1) == false);
assert(static_cast<bool>(opt2) == false);
swap(opt1, opt2);
assert(static_cast<bool>(opt1) == false);
assert(static_cast<bool>(opt2) == false);
}
{
T one{1};
optional<T&> opt1(one);
optional<T&> opt2;
static_assert(noexcept(swap(opt1, opt2)) == true);
assert(static_cast<bool>(opt1) == true);
assert(*opt1 == 1);
assert(std::addressof(*opt1) == std::addressof(one));
assert(static_cast<bool>(opt2) == false);
swap(opt1, opt2);
assert(static_cast<bool>(opt1) == false);
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 1);
assert(std::addressof(*opt2) == std::addressof(one));
}
{
T two{2};
optional<T&> opt1;
optional<T&> opt2(two);
static_assert(noexcept(swap(opt1, opt2)) == true);
assert(static_cast<bool>(opt1) == false);
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 2);
assert(std::addressof(*opt2) == std::addressof(two));
swap(opt1, opt2);
assert(static_cast<bool>(opt1) == true);
assert(*opt1 == 2);
assert(std::addressof(*opt1) == std::addressof(two));
assert(static_cast<bool>(opt2) == false);
}
{
T one{1};
T two{2};
optional<T&> opt1(one);
optional<T&> opt2(two);
static_assert(noexcept(swap(opt1, opt2)) == true);
assert(static_cast<bool>(opt1) == true);
assert(*opt1 == 1);
assert(std::addressof(*opt1) == std::addressof(one));
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 2);
assert(std::addressof(*opt2) == std::addressof(two));
swap(opt1, opt2);
assert(static_cast<bool>(opt1) == true);
assert(*opt1 == 2);
assert(std::addressof(*opt1) == std::addressof(two));
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 1);
assert(std::addressof(*opt2) == std::addressof(one));
}
return true;
}
#endif
int main(int, char**)
{
test_swap_sfinae();
#if TEST_STD_VER >= 26
static_assert(test_swap_ref<int>());
static_assert(test_swap_ref<double>());
test_swap_ref<int>();
test_swap_ref<double>();
#endif
{
optional<int> opt1;
optional<int> opt2;

View File

@ -1017,6 +1017,7 @@ feature_test_macros = [
"c++17": 201606,
"c++20": 202106, # P2231R1 Missing constexpr in std::optional and std::variant
"c++23": 202110, # P0798R8 Monadic operations for std::optional + LWG3621 Remove feature-test macro __cpp_lib_monadic_optional
"c++26": 202506, # P2988R12: std::optional<T&>
},
"headers": ["optional"],
},