blob: d137b3c40c7e6fca6532cd475c5049d7b90c5f72 [file] [edit]
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
#ifndef _LIBCPP___OPTIONAL_OPTIONAL_REF_H
#define _LIBCPP___OPTIONAL_OPTIONAL_REF_H
#include <__assert>
#include <__concepts/invocable.h>
#include <__config>
#include <__functional/invoke.h>
#include <__fwd/optional.h>
#include <__iterator/bounded_iter.h>
#include <__iterator/capacity_aware_iterator.h>
#include <__memory/addressof.h>
#include <__optional/common.h>
#include <__optional/comparison.h>
#include <__optional/hash.h>
#include <__optional/nullopt_t.h>
#include <__optional/swap.h>
#include <__type_traits/add_pointer.h>
#include <__type_traits/decay.h>
#include <__type_traits/invoke.h>
#include <__type_traits/is_array.h>
#include <__type_traits/is_constructible.h>
#include <__type_traits/is_convertible.h>
#include <__type_traits/is_nothrow_constructible.h>
#include <__type_traits/is_object.h>
#include <__type_traits/is_reference.h>
#include <__type_traits/is_same.h>
#include <__type_traits/is_trivially_relocatable.h>
#include <__type_traits/reference_constructs_from_temporary.h>
#include <__type_traits/remove_cv.h>
#include <__type_traits/remove_cvref.h>
#include <__type_traits/remove_reference.h>
#include <__utility/forward.h>
#include <__utility/in_place.h>
#include <__utility/move.h>
#include <__utility/swap.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif
_LIBCPP_PUSH_MACROS
#include <__undef_macros>
#if _LIBCPP_STD_VER >= 17
_LIBCPP_BEGIN_NAMESPACE_STD
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_;
_LIBCPP_HIDE_FROM_ABI constexpr __optional_storage_base() noexcept : __value_(nullptr) {}
template <class _Up>
_LIBCPP_HIDE_FROM_ABI constexpr void __convert_init_ref_val(_Up&& __val) {
_Tp& __r(std::forward<_Up>(__val));
__value_ = std::addressof(__r);
}
template <class _UArg>
_LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_storage_base(in_place_t, _UArg&& __uarg) {
static_assert(!__reference_constructs_from_temporary_v<_Tp, _UArg>,
"Attempted to construct a reference element in optional from a "
"possible temporary");
__convert_init_ref_val(std::forward<_UArg>(__uarg));
}
# if _LIBCPP_STD_VER >= 23
template <class _Fp, class... _Args>
constexpr __optional_storage_base(__optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args) {
__convert_init_ref_val(std::forward<invoke_result_t<_Fp, _Args...>>(
std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...)));
}
# endif
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void reset() noexcept { __value_ = nullptr; }
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return __value_ != nullptr; }
_LIBCPP_HIDE_FROM_ABI constexpr value_type& __get() const noexcept { return *__value_; }
template <class _UArg>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __construct(_UArg&& __val) {
static_assert(!__reference_constructs_from_temporary_v<_Tp, _UArg>,
"Attempted to construct a reference element in tuple from a "
"possible temporary");
__convert_init_ref_val(std::forward<_UArg>(__val));
}
template <class _That>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __construct_from(_That&& __opt) {
if (__opt.has_value())
__construct(std::forward<_That>(__opt).__get());
}
template <class _That>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __assign_from(_That&& __opt) {
if (has_value() == __opt.has_value()) {
if (has_value())
*__value_ = std::forward<_That>(__opt).__get();
} else {
if (has_value())
reset();
else
__construct(std::forward<_That>(__opt).__get());
}
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __swap(__optional_storage_base& __rhs) noexcept {
std::swap(__value_, __rhs.__value_);
}
// [optional.ref.observe]
_LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<_Tp> operator->() const noexcept {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator-> called on a disengaged value");
return std::addressof(this->__get());
}
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() const noexcept {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
return this->__get();
}
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& value() const {
if (!this->has_value())
std::__throw_bad_optional_access();
return this->__get();
}
};
# if _LIBCPP_STD_VER >= 26
template <class _Tp>
struct __optional_iterator_base;
template <class _Tp>
struct __optional_iterator_base<_Tp&> : __optional_storage_base<_Tp&> {
using __optional_storage_base<_Tp&>::__optional_storage_base;
};
# if _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
template <class _Tp>
requires(is_object_v<_Tp> && !__is_unbounded_array_v<_Tp>)
struct __optional_iterator_base<_Tp&> : __optional_storage_base<_Tp&> {
private:
using __pointer _LIBCPP_NODEBUG = add_pointer_t<_Tp>;
public:
using __optional_storage_base<_Tp&>::__optional_storage_base;
# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
using iterator = __bounded_iter<__pointer>;
# else
using iterator = __capacity_aware_iterator<__pointer, optional<_Tp&>, 1>;
# endif
// [optional.ref.iterators], iterator support
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const noexcept {
auto* __ptr = this->has_value() ? std::addressof(this->__get()) : nullptr;
# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
return std::__make_bounded_iter(__ptr, __ptr, __ptr + (this->has_value() ? 1 : 0));
# else
return std::__make_capacity_aware_iterator<__pointer, optional<_Tp&>, 1>(__ptr);
# endif
}
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const noexcept {
return begin() + (this->has_value() ? 1 : 0);
}
};
# endif
template <class _Tp>
class optional<_Tp&> : public __optional_iterator_base<_Tp&> {
using __base _LIBCPP_NODEBUG = __optional_iterator_base<_Tp&>;
template <class _Up, class _QualUp>
static constexpr bool __check_optionalU_ctor =
!is_same_v<remove_cv_t<_Tp>, optional<_Up>> && !is_same_v<_Tp&, _Up> && is_constructible_v<_Tp&, _QualUp>;
static_assert(!is_same_v<remove_cv_t<_Tp>, in_place_t>, "instantiation of optional with in_place_t is ill-formed");
static_assert(!is_same_v<remove_cv_t<_Tp>, nullopt_t>, "instantiation of optional with nullopt_t is ill-formed");
public:
using value_type = _Tp;
constexpr optional() noexcept = default;
constexpr optional(nullopt_t) noexcept {}
constexpr optional(const optional&) noexcept = default;
template <class _Arg>
requires(is_constructible_v<_Tp&, _Arg> && !reference_constructs_from_temporary_v<_Tp&, _Arg>)
constexpr explicit optional(in_place_t, _Arg&& __arg) : __base(in_place, std::forward<_Arg>(__arg)) {}
template <class _Up>
requires(!is_same_v<remove_cvref_t<_Up>, optional> && !is_same_v<remove_cvref_t<_Up>, in_place_t> &&
is_constructible_v<_Tp&, _Up> && !reference_constructs_from_temporary_v<_Tp&, _Up>)
constexpr explicit(!is_convertible_v<_Up, _Tp&>) optional(_Up&& __v) noexcept(is_nothrow_constructible_v<_Tp&, _Up>)
: __base(in_place, std::forward<_Up>(__v)) {}
template <class _Up>
requires(__check_optionalU_ctor<_Up, _Up&> && !reference_constructs_from_temporary_v<_Tp&, _Up&>)
constexpr explicit(!is_convertible_v<_Up&, _Tp&>)
optional(optional<_Up>& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, _Up&>) {
this->__construct_from(__rhs);
}
template <class _Up>
requires(__check_optionalU_ctor<_Up, const _Up&> && !reference_constructs_from_temporary_v<_Tp&, const _Up&>)
constexpr explicit(!is_convertible_v<const _Up&, _Tp&>)
optional(const optional<_Up>& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, const _Up&>) {
this->__construct_from(__rhs);
}
template <class _Up>
requires(__check_optionalU_ctor<_Up, _Up> && !reference_constructs_from_temporary_v<_Tp&, _Up>)
constexpr explicit(!is_convertible_v<_Up, _Tp&>)
optional(optional<_Up>&& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, _Up>) {
this->__construct_from(std::move(__rhs));
}
template <class _Up>
requires(__check_optionalU_ctor<_Up, const _Up> && !reference_constructs_from_temporary_v<_Tp&, const _Up>)
constexpr explicit(!is_convertible_v<const _Up, _Tp&>)
optional(const optional<_Up>&& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, const _Up>) {
this->__construct_from(std::move(__rhs));
}
template <class _Tag, class _Fp, class... _Args>
requires(is_same_v<_Tag, __optional_construct_from_invoke_tag>)
_LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_Tag, _Fp&& __f, _Args&&... __args)
: __base(__optional_construct_from_invoke_tag{}, std::forward<_Fp>(__f), std::forward<_Args>(__args)...) {}
// deleted overloads
template <class _Up>
requires(!is_same_v<remove_cvref_t<_Up>, optional> && !is_same_v<remove_cvref_t<_Up>, in_place_t> &&
is_constructible_v<_Tp&, _Up> && reference_constructs_from_temporary_v<_Tp&, _Up>)
constexpr explicit(!is_convertible_v<_Up, _Tp&>)
optional(_Up&& __v) noexcept(is_nothrow_constructible_v<_Tp&, _Up>) = delete;
template <class _Up>
requires(__check_optionalU_ctor<_Up, _Up&> && reference_constructs_from_temporary_v<_Tp&, _Up&>)
constexpr explicit(!is_convertible_v<_Up&, _Tp&>)
optional(optional<_Up>& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, _Up&>) = delete;
template <class _Up>
requires(__check_optionalU_ctor<_Up, const _Up&> && reference_constructs_from_temporary_v<_Tp&, const _Up&>)
constexpr explicit(!is_convertible_v<const _Up&, _Tp&>)
optional(const optional<_Up>& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, const _Up&>) = delete;
template <class _Up>
requires(__check_optionalU_ctor<_Up, _Up> && reference_constructs_from_temporary_v<_Tp&, _Up>)
constexpr explicit(!is_convertible_v<_Up, _Tp&>)
optional(optional<_Up>&& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, _Up>) = delete;
template <class _Up>
requires(__check_optionalU_ctor<_Up, const _Up> && reference_constructs_from_temporary_v<_Tp&, const _Up>)
constexpr explicit(!is_convertible_v<const _Up, _Tp&>)
optional(const optional<_Up>&& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, const _Up>) = delete;
constexpr ~optional() = default;
using __base::__get;
_LIBCPP_HIDE_FROM_ABI constexpr optional& operator=(nullopt_t) noexcept {
reset();
return *this;
}
constexpr optional& operator=(const optional&) noexcept = default;
template <class _Up>
requires(is_constructible_v<_Tp&, _Up> && !reference_constructs_from_temporary_v<_Tp&, _Up>)
constexpr _Tp& emplace(_Up&& __u) noexcept(is_nothrow_constructible_v<_Tp&, _Up>) {
this->__construct(std::forward<_Up>(__u));
return this->__get();
}
constexpr void swap(optional& __rhs) noexcept { this->__swap(__rhs); }
using __base::operator->;
using __base::operator*;
constexpr explicit operator bool() const noexcept { return has_value(); }
using __base::has_value;
using __base::value;
template <class _Up = remove_cv_t<_Tp>>
requires(!is_array_v<_Tp> && is_object_v<_Tp>)
[[nodiscard]] constexpr decay_t<_Tp> value_or(_Up&& __v) const {
using _XTp = remove_cv_t<_Tp>;
static_assert(is_constructible_v<_XTp, _Tp&>, "optional<T&>::value_or: remove_cv_t<T> must be constructible");
static_assert(is_convertible_v<_Up, _XTp>, "optional<T&>::value_or: U must be convertible to remove_cv_t<T>");
return this->has_value() ? this->__get() : static_cast<_XTp>(std::forward<_Up>(__v));
}
template <class _Func>
[[nodiscard]] constexpr auto and_then(_Func&& __f) const {
using _Up = invoke_result_t<_Func, _Tp&>;
static_assert(
__is_std_optional_v<remove_cvref_t<_Up>>, "Result of f(value()) must be a specialization of std::optional");
if (*this)
return std::invoke(std::forward<_Func>(__f), value());
return remove_cvref_t<_Up>();
}
template <class _Func>
[[nodiscard]] constexpr optional<remove_cv_t<invoke_result_t<_Func, _Tp&>>> transform(_Func&& __f) const {
using _Up = remove_cv_t<invoke_result_t<_Func, _Tp&>>;
static_assert(!is_array_v<_Up>, "Result of f(value()) should not be an Array");
static_assert(!is_same_v<_Up, in_place_t>, "Result of f(value()) should not be std::in_place_t");
static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(value()) should not be std::nullopt_t");
static_assert(
__is_valid_optional_contained_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
if (*this)
return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), value());
return optional<_Up>();
}
template <invocable _Func>
[[nodiscard]] constexpr optional or_else(_Func&& __f) const {
static_assert(is_same_v<remove_cvref_t<invoke_result_t<_Func>>, optional>,
"Result of f() should be the same type as this optional");
if (*this)
return *this;
return std::forward<_Func>(__f)();
}
using __base::reset;
};
# endif // _LIBCPP_STD_VER >= 26
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP_STD_VER >= 17
_LIBCPP_POP_MACROS
#endif // _LIBCPP___OPTIONAL_OPTIONAL_REF_H