Changeset View
Standalone View
libcxx/include/optional
Show First 20 Lines • Show All 126 Lines • ▼ Show 20 Lines | public: | |||||||||||||||||||||||||||||
constexpr bool has_value() const noexcept; | constexpr bool has_value() const noexcept; | |||||||||||||||||||||||||||||
constexpr T const &value() const &; | constexpr T const &value() const &; | |||||||||||||||||||||||||||||
constexpr T &value() &; | constexpr T &value() &; | |||||||||||||||||||||||||||||
constexpr T &&value() &&; | constexpr T &&value() &&; | |||||||||||||||||||||||||||||
constexpr const T &&value() const &&; | constexpr const T &&value() const &&; | |||||||||||||||||||||||||||||
template <class U> constexpr T value_or(U &&) const &; | template <class U> constexpr T value_or(U &&) const &; | |||||||||||||||||||||||||||||
template <class U> constexpr T value_or(U &&) &&; | template <class U> constexpr T value_or(U &&) &&; | |||||||||||||||||||||||||||||
// [optional.monadic], monadic operations | ||||||||||||||||||||||||||||||
template<class F> constexpr auto and_then(F&& f) &; // since C++23 | ||||||||||||||||||||||||||||||
template<class F> constexpr auto and_then(F&& f) &&; // since C++23 | ||||||||||||||||||||||||||||||
template<class F> constexpr auto and_then(F&& f) const&; // since C++23 | ||||||||||||||||||||||||||||||
template<class F> constexpr auto and_then(F&& f) const&&; // since C++23 | ||||||||||||||||||||||||||||||
template<class F> constexpr auto transform(F&& f) &; // since C++23 | ||||||||||||||||||||||||||||||
template<class F> constexpr auto transform(F&& f) &&; // since C++23 | ||||||||||||||||||||||||||||||
template<class F> constexpr auto transform(F&& f) const&; // since C++23 | ||||||||||||||||||||||||||||||
template<class F> constexpr auto transform(F&& f) const&&; // since C++23 | ||||||||||||||||||||||||||||||
template<class F> constexpr optional or_else(F&& f) &&; // since C++23 | ||||||||||||||||||||||||||||||
template<class F> constexpr optional or_else(F&& f) const&; // since C++23 | ||||||||||||||||||||||||||||||
ldionneUnsubmitted Done ReplyInline Actions
ldionne: | ||||||||||||||||||||||||||||||
// 23.6.3.6, modifiers | // 23.6.3.6, modifiers | |||||||||||||||||||||||||||||
void reset() noexcept; // constexpr in C++20 | void reset() noexcept; // constexpr in C++20 | |||||||||||||||||||||||||||||
private: | private: | |||||||||||||||||||||||||||||
T *val; // exposition only | T *val; // exposition only | |||||||||||||||||||||||||||||
}; | }; | |||||||||||||||||||||||||||||
template<class T> | template<class T> | |||||||||||||||||||||||||||||
optional(T) -> optional<T>; | optional(T) -> optional<T>; | |||||||||||||||||||||||||||||
} // namespace std | } // namespace std | |||||||||||||||||||||||||||||
*/ | */ | |||||||||||||||||||||||||||||
#include <__availability> | #include <__availability> | |||||||||||||||||||||||||||||
#include <__concepts/invocable.h> | ||||||||||||||||||||||||||||||
#include <__config> | #include <__config> | |||||||||||||||||||||||||||||
#include <__debug> | #include <__debug> | |||||||||||||||||||||||||||||
#include <__functional_base> | #include <__functional_base> | |||||||||||||||||||||||||||||
#include <compare> | #include <compare> | |||||||||||||||||||||||||||||
#include <functional> | #include <functional> | |||||||||||||||||||||||||||||
#include <initializer_list> | #include <initializer_list> | |||||||||||||||||||||||||||||
#include <new> | #include <new> | |||||||||||||||||||||||||||||
#include <stdexcept> | #include <stdexcept> | |||||||||||||||||||||||||||||
Show All 37 Lines | ||||||||||||||||||||||||||||||
struct nullopt_t | struct nullopt_t | |||||||||||||||||||||||||||||
{ | { | |||||||||||||||||||||||||||||
struct __secret_tag { _LIBCPP_INLINE_VISIBILITY explicit __secret_tag() = default; }; | struct __secret_tag { _LIBCPP_INLINE_VISIBILITY explicit __secret_tag() = default; }; | |||||||||||||||||||||||||||||
_LIBCPP_INLINE_VISIBILITY constexpr explicit nullopt_t(__secret_tag, __secret_tag) noexcept {} | _LIBCPP_INLINE_VISIBILITY constexpr explicit nullopt_t(__secret_tag, __secret_tag) noexcept {} | |||||||||||||||||||||||||||||
}; | }; | |||||||||||||||||||||||||||||
inline constexpr nullopt_t nullopt{nullopt_t::__secret_tag{}, nullopt_t::__secret_tag{}}; | inline constexpr nullopt_t nullopt{nullopt_t::__secret_tag{}, nullopt_t::__secret_tag{}}; | |||||||||||||||||||||||||||||
struct __optional_construct_from_invoke_tag {}; | ||||||||||||||||||||||||||||||
template <class _Tp, bool = is_trivially_destructible<_Tp>::value> | template <class _Tp, bool = is_trivially_destructible<_Tp>::value> | |||||||||||||||||||||||||||||
struct __optional_destruct_base; | struct __optional_destruct_base; | |||||||||||||||||||||||||||||
template <class _Tp> | template <class _Tp> | |||||||||||||||||||||||||||||
struct __optional_destruct_base<_Tp, false> | struct __optional_destruct_base<_Tp, false> | |||||||||||||||||||||||||||||
{ | { | |||||||||||||||||||||||||||||
typedef _Tp value_type; | typedef _Tp value_type; | |||||||||||||||||||||||||||||
static_assert(is_object_v<value_type>, | static_assert(is_object_v<value_type>, | |||||||||||||||||||||||||||||
Show All 18 Lines | constexpr __optional_destruct_base() noexcept | |||||||||||||||||||||||||||||
__engaged_(false) {} | __engaged_(false) {} | |||||||||||||||||||||||||||||
template <class... _Args> | template <class... _Args> | |||||||||||||||||||||||||||||
_LIBCPP_INLINE_VISIBILITY | _LIBCPP_INLINE_VISIBILITY | |||||||||||||||||||||||||||||
constexpr explicit __optional_destruct_base(in_place_t, _Args&&... __args) | constexpr explicit __optional_destruct_base(in_place_t, _Args&&... __args) | |||||||||||||||||||||||||||||
: __val_(_VSTD::forward<_Args>(__args)...), | : __val_(_VSTD::forward<_Args>(__args)...), | |||||||||||||||||||||||||||||
__engaged_(true) {} | __engaged_(true) {} | |||||||||||||||||||||||||||||
#if _LIBCPP_STD_VER > 20 | ||||||||||||||||||||||||||||||
template <class _Fp, class... _Args> | ||||||||||||||||||||||||||||||
_LIBCPP_HIDE_FROM_ABI | ||||||||||||||||||||||||||||||
constexpr __optional_destruct_base(__optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args) | ||||||||||||||||||||||||||||||
: __val_(_VSTD::invoke(_VSTD::forward<_Fp>(__f), _VSTD::forward<_Args>(__args)...)), __engaged_(true) {} | ||||||||||||||||||||||||||||||
#endif | ||||||||||||||||||||||||||||||
Nit: match {} brace style from lines 244 and 250. (Ditto line 299.) Quuxplusone: Nit: match `{}` brace style from lines 244 and 250. (Ditto line 299.) | ||||||||||||||||||||||||||||||
_LIBCPP_INLINE_VISIBILITY | _LIBCPP_INLINE_VISIBILITY | |||||||||||||||||||||||||||||
_LIBCPP_CONSTEXPR_AFTER_CXX17 void reset() noexcept | _LIBCPP_CONSTEXPR_AFTER_CXX17 void reset() noexcept | |||||||||||||||||||||||||||||
{ | { | |||||||||||||||||||||||||||||
if (__engaged_) | if (__engaged_) | |||||||||||||||||||||||||||||
{ | { | |||||||||||||||||||||||||||||
__val_.~value_type(); | __val_.~value_type(); | |||||||||||||||||||||||||||||
__engaged_ = false; | __engaged_ = false; | |||||||||||||||||||||||||||||
} | } | |||||||||||||||||||||||||||||
Show All 19 Lines | constexpr __optional_destruct_base() noexcept | |||||||||||||||||||||||||||||
__engaged_(false) {} | __engaged_(false) {} | |||||||||||||||||||||||||||||
template <class... _Args> | template <class... _Args> | |||||||||||||||||||||||||||||
_LIBCPP_INLINE_VISIBILITY | _LIBCPP_INLINE_VISIBILITY | |||||||||||||||||||||||||||||
constexpr explicit __optional_destruct_base(in_place_t, _Args&&... __args) | constexpr explicit __optional_destruct_base(in_place_t, _Args&&... __args) | |||||||||||||||||||||||||||||
: __val_(_VSTD::forward<_Args>(__args)...), | : __val_(_VSTD::forward<_Args>(__args)...), | |||||||||||||||||||||||||||||
__engaged_(true) {} | __engaged_(true) {} | |||||||||||||||||||||||||||||
#if _LIBCPP_STD_VER > 20 | ||||||||||||||||||||||||||||||
template <class _Fp, class... _Args> | ||||||||||||||||||||||||||||||
_LIBCPP_HIDE_FROM_ABI | ||||||||||||||||||||||||||||||
constexpr __optional_destruct_base(__optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args) | ||||||||||||||||||||||||||||||
: __val_(_VSTD::invoke(_VSTD::forward<_Fp>(__f), _VSTD::forward<_Args>(__args)...)), __engaged_(true) {} | ||||||||||||||||||||||||||||||
#endif | ||||||||||||||||||||||||||||||
_LIBCPP_INLINE_VISIBILITY | _LIBCPP_INLINE_VISIBILITY | |||||||||||||||||||||||||||||
_LIBCPP_CONSTEXPR_AFTER_CXX17 void reset() noexcept | _LIBCPP_CONSTEXPR_AFTER_CXX17 void reset() noexcept | |||||||||||||||||||||||||||||
{ | { | |||||||||||||||||||||||||||||
if (__engaged_) | if (__engaged_) | |||||||||||||||||||||||||||||
{ | { | |||||||||||||||||||||||||||||
__engaged_ = false; | __engaged_ = false; | |||||||||||||||||||||||||||||
} | } | |||||||||||||||||||||||||||||
} | } | |||||||||||||||||||||||||||||
▲ Show 20 Lines • Show All 297 Lines • ▼ Show 20 Lines | ||||||||||||||||||||||||||||||
>; | >; | |||||||||||||||||||||||||||||
template <class _Tp> | template <class _Tp> | |||||||||||||||||||||||||||||
using __optional_sfinae_assign_base_t = __sfinae_assign_base< | using __optional_sfinae_assign_base_t = __sfinae_assign_base< | |||||||||||||||||||||||||||||
(is_copy_constructible<_Tp>::value && is_copy_assignable<_Tp>::value), | (is_copy_constructible<_Tp>::value && is_copy_assignable<_Tp>::value), | |||||||||||||||||||||||||||||
(is_move_constructible<_Tp>::value && is_move_assignable<_Tp>::value) | (is_move_constructible<_Tp>::value && is_move_assignable<_Tp>::value) | |||||||||||||||||||||||||||||
>; | >; | |||||||||||||||||||||||||||||
template <class _Tp> | template<class _Tp> | |||||||||||||||||||||||||||||
I think it is fine to unconditionally define this template. We'll only be using it in C++20, but I don't think it's worth the complexity to add the #if. ldionne: I think it is fine to unconditionally define this template. We'll only be using it in C++20… | ||||||||||||||||||||||||||||||
class optional; | ||||||||||||||||||||||||||||||
template <class _Tp> | ||||||||||||||||||||||||||||||
struct __is_std_optional : false_type {}; | ||||||||||||||||||||||||||||||
template <class _Tp> struct __is_std_optional<optional<_Tp>> : true_type {}; | ||||||||||||||||||||||||||||||
We generally don't qualify types in namespace std. So just false_type would be consistent with what we do everywhere. This applies throughout the patch. ldionne: We generally don't qualify types in namespace `std`. So just `false_type` would be consistent… | ||||||||||||||||||||||||||||||
template <class _Tp> | ||||||||||||||||||||||||||||||
class optional | class optional | |||||||||||||||||||||||||||||
I suggest __is_std_optional for consistency with __is_std_{span,array}. I also think there's no need for the __is_optional_v variable template (again for consistency). (Or, if you absolutely want the variable template for efficiency — which you shouldn't — then I'd say to get rid of the struct template and just specialize the variable template directly.) Quuxplusone: I suggest `__is_std_optional` for consistency with `__is_std_{span,array}`. I also think… | ||||||||||||||||||||||||||||||
: private __optional_move_assign_base<_Tp> | : private __optional_move_assign_base<_Tp> | |||||||||||||||||||||||||||||
, private __optional_sfinae_ctor_base_t<_Tp> | , private __optional_sfinae_ctor_base_t<_Tp> | |||||||||||||||||||||||||||||
, private __optional_sfinae_assign_base_t<_Tp> | , private __optional_sfinae_assign_base_t<_Tp> | |||||||||||||||||||||||||||||
{ | { | |||||||||||||||||||||||||||||
using __base = __optional_move_assign_base<_Tp>; | using __base = __optional_move_assign_base<_Tp>; | |||||||||||||||||||||||||||||
public: | public: | |||||||||||||||||||||||||||||
using value_type = _Tp; | using value_type = _Tp; | |||||||||||||||||||||||||||||
▲ Show 20 Lines • Show All 84 Lines • ▼ Show 20 Lines | using _CheckOptionalLikeAssign = _If< | |||||||||||||||||||||||||||||
_And< | _And< | |||||||||||||||||||||||||||||
_IsNotSame<_Up, _Tp>, | _IsNotSame<_Up, _Tp>, | |||||||||||||||||||||||||||||
is_constructible<_Tp, _QualUp>, | is_constructible<_Tp, _QualUp>, | |||||||||||||||||||||||||||||
is_assignable<_Tp&, _QualUp> | is_assignable<_Tp&, _QualUp> | |||||||||||||||||||||||||||||
>::value, | >::value, | |||||||||||||||||||||||||||||
_CheckOptionalLikeConstructor<_QualUp>, | _CheckOptionalLikeConstructor<_QualUp>, | |||||||||||||||||||||||||||||
__check_tuple_constructor_fail | __check_tuple_constructor_fail | |||||||||||||||||||||||||||||
>; | >; | |||||||||||||||||||||||||||||
public: | public: | |||||||||||||||||||||||||||||
_LIBCPP_INLINE_VISIBILITY constexpr optional() noexcept {} | _LIBCPP_INLINE_VISIBILITY constexpr optional() noexcept {} | |||||||||||||||||||||||||||||
_LIBCPP_INLINE_VISIBILITY constexpr optional(const optional&) = default; | _LIBCPP_INLINE_VISIBILITY constexpr optional(const optional&) = default; | |||||||||||||||||||||||||||||
_LIBCPP_INLINE_VISIBILITY constexpr optional(optional&&) = default; | _LIBCPP_INLINE_VISIBILITY constexpr optional(optional&&) = default; | |||||||||||||||||||||||||||||
_LIBCPP_INLINE_VISIBILITY constexpr optional(nullopt_t) noexcept {} | _LIBCPP_INLINE_VISIBILITY constexpr optional(nullopt_t) noexcept {} | |||||||||||||||||||||||||||||
template <class _InPlaceT, class... _Args, class = enable_if_t< | template <class _InPlaceT, class... _Args, class = enable_if_t< | |||||||||||||||||||||||||||||
▲ Show 20 Lines • Show All 59 Lines • ▼ Show 20 Lines | template <class _Up, enable_if_t< | |||||||||||||||||||||||||||||
_CheckOptionalLikeCtor<_Up, _Up &&>::template __enable_explicit<_Up>() | _CheckOptionalLikeCtor<_Up, _Up &&>::template __enable_explicit<_Up>() | |||||||||||||||||||||||||||||
, int> = 0> | , int> = 0> | |||||||||||||||||||||||||||||
_LIBCPP_INLINE_VISIBILITY | _LIBCPP_INLINE_VISIBILITY | |||||||||||||||||||||||||||||
_LIBCPP_CONSTEXPR_AFTER_CXX17 explicit optional(optional<_Up>&& __v) | _LIBCPP_CONSTEXPR_AFTER_CXX17 explicit optional(optional<_Up>&& __v) | |||||||||||||||||||||||||||||
{ | { | |||||||||||||||||||||||||||||
this->__construct_from(_VSTD::move(__v)); | this->__construct_from(_VSTD::move(__v)); | |||||||||||||||||||||||||||||
} | } | |||||||||||||||||||||||||||||
#if _LIBCPP_STD_VER > 20 | ||||||||||||||||||||||||||||||
template<class _Fp, class... _Args> | ||||||||||||||||||||||||||||||
_LIBCPP_HIDE_FROM_ABI | ||||||||||||||||||||||||||||||
constexpr explicit optional(__optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args) | ||||||||||||||||||||||||||||||
: __base(__optional_construct_from_invoke_tag{}, _VSTD::forward<_Fp>(__f), _VSTD::forward<_Args>(__args)...) { | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
#endif | ||||||||||||||||||||||||||||||
_LIBCPP_INLINE_VISIBILITY | _LIBCPP_INLINE_VISIBILITY | |||||||||||||||||||||||||||||
_LIBCPP_CONSTEXPR_AFTER_CXX17 optional& operator=(nullopt_t) noexcept | _LIBCPP_CONSTEXPR_AFTER_CXX17 optional& operator=(nullopt_t) noexcept | |||||||||||||||||||||||||||||
{ | { | |||||||||||||||||||||||||||||
reset(); | reset(); | |||||||||||||||||||||||||||||
return *this; | return *this; | |||||||||||||||||||||||||||||
} | } | |||||||||||||||||||||||||||||
_LIBCPP_INLINE_VISIBILITY optional& operator=(const optional&) = default; | _LIBCPP_INLINE_VISIBILITY optional& operator=(const optional&) = default; | |||||||||||||||||||||||||||||
▲ Show 20 Lines • Show All 218 Lines • ▼ Show 20 Lines | constexpr value_type value_or(_Up&& __v) && | |||||||||||||||||||||||||||||
static_assert(is_move_constructible_v<value_type>, | static_assert(is_move_constructible_v<value_type>, | |||||||||||||||||||||||||||||
"optional<T>::value_or: T must be move constructible"); | "optional<T>::value_or: T must be move constructible"); | |||||||||||||||||||||||||||||
static_assert(is_convertible_v<_Up, value_type>, | static_assert(is_convertible_v<_Up, value_type>, | |||||||||||||||||||||||||||||
"optional<T>::value_or: U must be convertible to T"); | "optional<T>::value_or: U must be convertible to T"); | |||||||||||||||||||||||||||||
return this->has_value() ? _VSTD::move(this->__get()) : | return this->has_value() ? _VSTD::move(this->__get()) : | |||||||||||||||||||||||||||||
static_cast<value_type>(_VSTD::forward<_Up>(__v)); | static_cast<value_type>(_VSTD::forward<_Up>(__v)); | |||||||||||||||||||||||||||||
} | } | |||||||||||||||||||||||||||||
#if _LIBCPP_STD_VER > 20 | ||||||||||||||||||||||||||||||
template<class _Func> | ||||||||||||||||||||||||||||||
_LIBCPP_HIDE_FROM_ABI | ||||||||||||||||||||||||||||||
constexpr auto and_then(_Func&& __f) & { | ||||||||||||||||||||||||||||||
using _Up = invoke_result_t<_Func, value_type&>; | ||||||||||||||||||||||||||||||
static_assert(__is_std_optional<remove_cvref_t<_Up>>::value, | ||||||||||||||||||||||||||||||
"Result of f(value()) must be a specialization of std::optional"); | ||||||||||||||||||||||||||||||
if (*this) | ||||||||||||||||||||||||||||||
return _VSTD::invoke(_VSTD::forward<_Func>(__f), value()); | ||||||||||||||||||||||||||||||
return remove_cvref_t<_Up>(); | ||||||||||||||||||||||||||||||
Everywhere (for consistency). ldionne: Everywhere (for consistency). | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
template<class _Func> | ||||||||||||||||||||||||||||||
_LIBCPP_HIDE_FROM_ABI | ||||||||||||||||||||||||||||||
constexpr auto and_then(_Func&& __f) const& { | ||||||||||||||||||||||||||||||
using _Up = invoke_result_t<_Func, const value_type&>; | ||||||||||||||||||||||||||||||
Style nit: The Standard calls this U, and I think we should call it _Up. __lower_snake_case looks too much like a local variable to me (and is also verboser than needed). (Alternatively, _Rp; but I think it makes sense to follow the Standard's U.) Quuxplusone: Style nit: The Standard calls this `U`, and I think we should call it `_Up`. | ||||||||||||||||||||||||||||||
static_assert(__is_std_optional<remove_cvref_t<_Up>>::value, | ||||||||||||||||||||||||||||||
The spec says:
I think we should be checking this instead: static_assert(__is_std_optional<remove_cvref_t<_Up>>::value, ...); This comment probably applies elsewhere. ldionne: The spec says:
> Let `U` be `invoke_result_t<F, decltype(std::move(value()))>`.
> Mandates… | ||||||||||||||||||||||||||||||
"Result of f(value()) must be a specialization of std::optional"); | ||||||||||||||||||||||||||||||
I wonder if this should say a specialization of std::optional, without the T, for pedantic correctness. This can totally be done post-commit, though, if we want to, rather than holding up this PR waiting for a round-trip to see if @ldionne agrees or whatever. :) Quuxplusone: I wonder if this should say `a specialization of std::optional`, without the `T`, for pedantic… | ||||||||||||||||||||||||||||||
Yeah, I agree, let's reword the warning. ldionne: Yeah, I agree, let's reword the warning. | ||||||||||||||||||||||||||||||
if (*this) | ||||||||||||||||||||||||||||||
return _VSTD::invoke(_VSTD::forward<_Func>(__f), value()); | ||||||||||||||||||||||||||||||
return remove_cvref_t<_Up>(); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
template<class _Func> | ||||||||||||||||||||||||||||||
_LIBCPP_HIDE_FROM_ABI | ||||||||||||||||||||||||||||||
constexpr auto and_then(_Func&& __f) && { | ||||||||||||||||||||||||||||||
using _Up = invoke_result_t<_Func, value_type&&>; | ||||||||||||||||||||||||||||||
Note that this qualification of _VSTD::move is good because it protects against ADL. My comment above about qualifications do not apply to cases that prevent ADL from being triggered. ldionne: Note that this qualification of `_VSTD::move` is good because it protects against ADL. My… | ||||||||||||||||||||||||||||||
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) | ||||||||||||||||||||||||||||||
return _VSTD::invoke(_VSTD::forward<_Func>(__f), _VSTD::move(value())); | ||||||||||||||||||||||||||||||
return remove_cvref_t<_Up>(); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
template<class _Func> | ||||||||||||||||||||||||||||||
_LIBCPP_HIDE_FROM_ABI | ||||||||||||||||||||||||||||||
constexpr auto and_then(_Func&& __f) const&& { | ||||||||||||||||||||||||||||||
using _Up = invoke_result_t<_Func, const value_type&&>; | ||||||||||||||||||||||||||||||
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) | ||||||||||||||||||||||||||||||
return _VSTD::invoke(_VSTD::forward<_Func>(__f), _VSTD::move(value())); | ||||||||||||||||||||||||||||||
return remove_cvref_t<_Up>(); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
template<class _Func> | ||||||||||||||||||||||||||||||
_LIBCPP_HIDE_FROM_ABI | ||||||||||||||||||||||||||||||
constexpr auto transform(_Func&& __f) & { | ||||||||||||||||||||||||||||||
using _Up = remove_cv_t<invoke_result_t<_Func, value_type&>>; | ||||||||||||||||||||||||||||||
This should be remove_cv_t. Ditto for other instances of transform. tcanens: This should be `remove_cv_t`. Ditto for other instances of `transform`. | ||||||||||||||||||||||||||||||
static_assert(!is_array_v<_Up>, "Result of f(value()) should not be an Array"); | ||||||||||||||||||||||||||||||
The paper says:
Here we're checking that it's not an array, it's not in_place_t, and it's not nullopt_t. But we are not checking that it's an object type. For instance, if __result_type ends up being a function type or void, we won't trigger any static_assert. I'm pretty sure it's going to fail in some other way, but I still think we should check for it explicitly. ldionne: The paper says:
> Mandates: `U` is a non-array object type other than `in_place_t` or… | ||||||||||||||||||||||||||||||
static_assert(!is_same_v<_Up, in_place_t>, | ||||||||||||||||||||||||||||||
"Result of f(value()) should not be std::in_place_t"); | ||||||||||||||||||||||||||||||
static_assert(!is_same_v<_Up, nullopt_t>, | ||||||||||||||||||||||||||||||
"Result of f(value()) should not be std::nullopt_t"); | ||||||||||||||||||||||||||||||
static_assert(is_object_v<_Up>, "Result of f(value()) should be an object type"); | ||||||||||||||||||||||||||||||
if (*this) | ||||||||||||||||||||||||||||||
This isn't what the paper is expressing. The full wording of the paper is:
What they mean in the Returns: section is that the object inside the optional should be direct-non-list-initialized as-if one had used U u(invoke(std::forward<F>(f), value())); exactly. Combined with the note that U doesn't have to be move-constructible, I am fairly convinced they are telling us to allow move-elision, like what I did in D107932. At the end of the day, I suspect this will have to look something like optional<__result_type>(__construct_from_invoke_tag{}, _VSTD::forward<_Func>(__f), value()); where there's a special optional constructor that looks like template<class _Fp, class ..._Args> constexpr explicit optional(__construct_from_invoke_tag, _Fp&& __f, _Args&& ...__args) : __value_(_VSTD::invoke(_VSTD::forward<_Fp>(__f), _VSTD::forward<_Args>(__args)...))) { } This is of course going to be more complicated due to inheritance and stuff, but you get the idea. Here, at least, we allow move elision to occur. Also, please add tests for all the methods that have this Note: where __result_type is not move-constructible. Those should fail with your current implementation and they should start working once you implement something like what I just laid out. ldionne: This isn't what the paper is expressing. The full wording of the paper is:
> Let `U` be… | ||||||||||||||||||||||||||||||
tcanens: > This isn't what the paper is expressing. The full wording of the paper is:
>
> > Let `U` be… | ||||||||||||||||||||||||||||||
I thought I dropped this one... anyway, I wrote this wording and yes, that's the intent. tcanens: I thought I dropped this one... anyway, I wrote this wording and yes, that's the intent. | ||||||||||||||||||||||||||||||
return optional<_Up>(__optional_construct_from_invoke_tag{}, _VSTD::forward<_Func>(__f), value()); | ||||||||||||||||||||||||||||||
return optional<_Up>(); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
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&>>; | ||||||||||||||||||||||||||||||
static_assert(!is_array_v<_Up>, "Result of f(value()) should not be an Array"); | ||||||||||||||||||||||||||||||
static_assert(!is_same_v<_Up, in_place_t>, | ||||||||||||||||||||||||||||||
"Result of f(value()) should not be std::in_place_t"); | ||||||||||||||||||||||||||||||
static_assert(!is_same_v<_Up, nullopt_t>, | ||||||||||||||||||||||||||||||
"Result of f(value()) should not be std::nullopt_t"); | ||||||||||||||||||||||||||||||
static_assert(is_object_v<_Up>, "Result of f(value()) should be an object type"); | ||||||||||||||||||||||||||||||
if (*this) | ||||||||||||||||||||||||||||||
return optional<_Up>(__optional_construct_from_invoke_tag{}, _VSTD::forward<_Func>(__f), value()); | ||||||||||||||||||||||||||||||
return optional<_Up>(); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
template<class _Func> | ||||||||||||||||||||||||||||||
_LIBCPP_HIDE_FROM_ABI | ||||||||||||||||||||||||||||||
Here decltype(std::move(value())) is just _Tp&& (and ditto throughout, with cvref-qualification differences). IMO the shorter _Tp&& form should be used for clarity. Quuxplusone: Here `decltype(std::move(value()))` is just `_Tp&&` (and ditto throughout, with cvref… | ||||||||||||||||||||||||||||||
constexpr auto transform(_Func&& __f) && { | ||||||||||||||||||||||||||||||
using _Up = remove_cv_t<invoke_result_t<_Func, value_type&&>>; | ||||||||||||||||||||||||||||||
static_assert(!is_array_v<_Up>, "Result of f(std::move(value())) should not be an Array"); | ||||||||||||||||||||||||||||||
static_assert(!is_same_v<_Up, in_place_t>, | ||||||||||||||||||||||||||||||
"Result of f(std::move(value())) should not be std::in_place_t"); | ||||||||||||||||||||||||||||||
static_assert(!is_same_v<_Up, nullopt_t>, | ||||||||||||||||||||||||||||||
"Result of f(std::move(value())) should not be std::nullopt_t"); | ||||||||||||||||||||||||||||||
static_assert(is_object_v<_Up>, "Result of f(std::move(value())) should be an object type"); | ||||||||||||||||||||||||||||||
if (*this) | ||||||||||||||||||||||||||||||
return optional<_Up>(__optional_construct_from_invoke_tag{}, _VSTD::forward<_Func>(__f), _VSTD::move(value())); | ||||||||||||||||||||||||||||||
return optional<_Up>(); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
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&&>>; | ||||||||||||||||||||||||||||||
static_assert(!is_array_v<_Up>, "Result of f(std::move(value())) should not be an Array"); | ||||||||||||||||||||||||||||||
static_assert(!is_same_v<_Up, in_place_t>, | ||||||||||||||||||||||||||||||
"Result of f(std::move(value())) should not be std::in_place_t"); | ||||||||||||||||||||||||||||||
static_assert(!is_same_v<_Up, nullopt_t>, | ||||||||||||||||||||||||||||||
"Result of f(std::move(value())) should not be std::nullopt_t"); | ||||||||||||||||||||||||||||||
static_assert(is_object_v<_Up>, "Result of f(std::move(value())) should be an object type"); | ||||||||||||||||||||||||||||||
if (*this) | ||||||||||||||||||||||||||||||
return optional<_Up>(__optional_construct_from_invoke_tag{}, _VSTD::forward<_Func>(__f), _VSTD::move(value())); | ||||||||||||||||||||||||||||||
return optional<_Up>(); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
template<invocable _Func> | ||||||||||||||||||||||||||||||
_LIBCPP_HIDE_FROM_ABI | ||||||||||||||||||||||||||||||
constexpr optional or_else(_Func&& __f) const& requires is_copy_constructible_v<value_type> { | ||||||||||||||||||||||||||||||
static_assert(is_same_v<remove_cvref_t<invoke_result_t<_Func>>, optional>, | ||||||||||||||||||||||||||||||
"Result of f() should be the same type as this optional"); | ||||||||||||||||||||||||||||||
Not just an optional - it needs to be this particular specialization of optional. tcanens: Not just an `optional` - it needs to be this particular specialization of `optional`. | ||||||||||||||||||||||||||||||
Please make this Result of f() should be the same type as this optional — note the addition of "type." It might not be "the same (object)" in the same sense as operator='s return type must be "the same as this optional." Adding type makes that unambiguous, and it's cheap to do. Quuxplusone: Please make this `Result of f() should be the same type as this optional` — note the addition… | ||||||||||||||||||||||||||||||
if (*this) | ||||||||||||||||||||||||||||||
return *this; | ||||||||||||||||||||||||||||||
return _VSTD::forward<_Func>(__f)(); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
template<invocable _Func> | ||||||||||||||||||||||||||||||
_LIBCPP_HIDE_FROM_ABI | ||||||||||||||||||||||||||||||
constexpr optional or_else(_Func&& __f) && requires is_move_constructible_v<value_type> { | ||||||||||||||||||||||||||||||
static_assert(is_same_v<remove_cvref_t<invoke_result_t<_Func>>, optional>, | ||||||||||||||||||||||||||||||
"Result of f() should be the same type as this optional"); | ||||||||||||||||||||||||||||||
if (*this) | ||||||||||||||||||||||||||||||
return _VSTD::move(*this); | ||||||||||||||||||||||||||||||
return _VSTD::forward<_Func>(__f)(); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
#endif // _LIBCPP_STD_VER > 20 | ||||||||||||||||||||||||||||||
using __base::reset; | using __base::reset; | |||||||||||||||||||||||||||||
}; | }; | |||||||||||||||||||||||||||||
#if _LIBCPP_STD_VER >= 17 | #if _LIBCPP_STD_VER >= 17 | |||||||||||||||||||||||||||||
template<class T> | template<class T> | |||||||||||||||||||||||||||||
optional(T) -> optional<T>; | optional(T) -> optional<T>; | |||||||||||||||||||||||||||||
#endif | #endif | |||||||||||||||||||||||||||||
▲ Show 20 Lines • Show All 394 Lines • Show Last 20 Lines |