[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:
parent
95f2728b5c
commit
389a23c538
@ -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``
|
||||
|
||||
@ -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>`__)
|
||||
|
||||
@ -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>`__",""
|
||||
|
||||
|
@ -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>
|
||||
|
||||
@ -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));
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 (&)()>>);
|
||||
|
||||
@ -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 (&)()>);
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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()>();
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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}}
|
||||
|
||||
@ -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>();
|
||||
|
||||
@ -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>&)
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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, "");
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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}}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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"
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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"],
|
||||
},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user