From 8a7583523f047c254f4c765a76e0d359614f17e6 Mon Sep 17 00:00:00 2001 From: Steve Downey Date: Thu, 15 Aug 2024 10:36:01 -0400 Subject: [PATCH] Version of optional for paper. --- papers/P2988/optional.hpp | 496 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 496 insertions(+) create mode 100644 papers/P2988/optional.hpp diff --git a/papers/P2988/optional.hpp b/papers/P2988/optional.hpp new file mode 100644 index 00000000..7262cd71 --- /dev/null +++ b/papers/P2988/optional.hpp @@ -0,0 +1,496 @@ +// ---------------------- +// BASE AND DETAILS ELIDED +// ---------------------- + +/****************/ +/* optional */ +/****************/ + +template +class optional { + public: + using value_type = T&; + using iterator = + detail::contiguous_iterator; // see [optionalref.iterators] + using const_iterator = + detail::contiguous_iterator; // see [optionalref.iterators] + + public: + // \ref{optionalref.ctor}, constructors + + constexpr optional() noexcept; + constexpr optional(nullopt_t) noexcept; + constexpr optional(const optional& rhs) noexcept = default; + constexpr optional(optional&& rhs) noexcept = default; + template + requires(!detail::is_optional>) + constexpr explicit(!is_convertible_v) optional(U&& u) noexcept; + template + constexpr explicit(!is_convertible_v) + optional(const optional& rhs) noexcept; + + // \ref{optionalref.dtor}, destructor + constexpr ~optional() = default; + + // \ref{optionalref.assign}, assignment + constexpr optional& operator=(nullopt_t) noexcept; + + constexpr optional& operator=(const optional& rhs) noexcept = default; + constexpr optional& operator=(optional&& rhs) noexcept = default; + + template + requires(!detail::is_optional>) + constexpr optional& operator=(U&& u); + + template + constexpr optional& operator=(const optional& rhs) noexcept; + + template + constexpr optional& operator=(optional&& rhs) = delete; + + template + requires(!detail::is_optional>) + constexpr optional& emplace(U&& u) noexcept; + + // \ref{optionalref.swap}, swap + constexpr void swap(optional& rhs) noexcept; + + // \ref{optional.iterators}, iterator support + constexpr iterator begin() noexcept; + constexpr const_iterator begin() const noexcept; + constexpr iterator end() noexcept; + constexpr const_iterator end() const noexcept; + + // \ref{optionalref.observe}, observers + constexpr T* operator->() const noexcept; + constexpr T& operator*() const noexcept; + constexpr explicit operator bool() const noexcept; + constexpr bool has_value() const noexcept; + constexpr T& value() const; + template + constexpr T value_or(U&& u) const; + + // \ref{optionalref.monadic}, monadic operations + template + constexpr auto and_then(F&& f) const; + template + constexpr auto transform(F&& f) const -> optional>; + template + constexpr optional or_else(F&& f) const; + + // \ref{optional.mod}, modifiers + constexpr void reset() noexcept; + + private: + T* value_; // exposition only +}; + +// \rSec3[optionalref.ctor]{Constructors} +template +constexpr optional::optional() noexcept : value_(nullptr) {} + +template +constexpr optional::optional(nullopt_t) noexcept : value_(nullptr) {} + +template +template + requires(!detail::is_optional>) +constexpr optional::optional(U&& u) noexcept : value_(addressof(u)) { + static_assert(is_constructible_v, U>, + "Must be able to bind U to T&"); + static_assert(is_lvalue_reference::value, "U must be an lvalue"); +} + +template +template +constexpr optional::optional(const optional& rhs) noexcept { + static_assert(is_constructible_v, U>, + "Must be able to bind U to T&"); + if (rhs.has_value()) + value_ = to_address(rhs); + else + value_ = nullptr; +} + +// \rSec3[optionalref.assign]{Assignment} +template +constexpr optional& optional::operator=(nullopt_t) noexcept { + value_ = nullptr; + return *this; +} + +template +template + requires(!detail::is_optional>) +constexpr optional& optional::operator=(U&& u) { + static_assert(is_constructible_v, U>, + "Must be able to bind U to T&"); + static_assert(is_lvalue_reference::value, "U must be an lvalue"); + value_ = addressof(u); + return *this; +} + +template +template +constexpr optional& +optional::operator=(const optional& rhs) noexcept { + static_assert(is_constructible_v, U>, + "Must be able to bind U to T&"); + if (rhs.has_value()) + value_ = to_address(rhs); + else + value_ = nullptr; + return *this; +} + +template +template + requires(!detail::is_optional>) +constexpr optional& optional::emplace(U&& u) noexcept { + return *this = std::forward(u); +} + +// \rSec3[optionalref.swap]{Swap} + +template +constexpr void optional::swap(optional& rhs) noexcept { + std::swap(value_, rhs.value_); +} + +// \rSec3[optionalref.iterators]{Iterator Support} +template +constexpr optional::iterator optional::begin() noexcept { + return iterator(has_value() ? value_ : nullptr); +}; + +template +constexpr optional::const_iterator optional::begin() const noexcept { + return const_iterator(has_value() ? value_ : nullptr); +}; + +template +constexpr optional::iterator optional::end() noexcept { + return begin() + has_value(); +} + +template +constexpr optional::const_iterator optional::end() const noexcept { + return begin() + has_value(); +} + +// \rSec3[optionalref.observe]{Observers} +template +constexpr T* optional::operator->() const noexcept { + return value_; +} + +template +constexpr T& optional::operator*() const noexcept { + return *value_; +} + +template +constexpr optional::operator bool() const noexcept { + return value_ != nullptr; +} +template +constexpr bool optional::has_value() const noexcept { + return value_ != nullptr; +} + +template +constexpr T& optional::value() const { + if (has_value()) + return *value_; + throw bad_optional_access(); +} + +template +template +constexpr T optional::value_or(U&& u) const { + static_assert(is_copy_constructible_v, "T must be copy constructible"); + static_assert(is_convertible_v, + "Must be able to convert u to T"); + return has_value() ? *value_ : std::forward(u); +} + +// \rSec3[optionalref.monadic]{Monadic operations} +template +template +constexpr auto optional::and_then(F&& f) const { + using U = invoke_result_t; + static_assert(detail::is_optional, "F must return an optional"); + return (has_value()) ? invoke(std::forward(f), *value_) + : remove_cvref_t(); +} + +template +template +constexpr auto +optional::transform(F&& f) const -> optional> { + using U = invoke_result_t; + return (has_value()) ? optional{invoke(std::forward(f), *value_)} + : optional{}; +} + +template +template +constexpr optional optional::or_else(F&& f) const { + using U = invoke_result_t; + static_assert(is_same_v, optional>); + return has_value() ? *value_ : std::forward(f)(); +} + +// \rSec3[optional.mod]{modifiers} +template +constexpr void optional::reset() noexcept { + value_ = nullptr; +} + +/*****************/ +/* optional */ +/*****************/ + +template +class optional { + public: + using value_type = T&&; + using iterator = + detail::contiguous_iterator; // see [optionalrref.iterators] + using const_iterator = + detail::contiguous_iterator; // see [optionalrref.iterators] + + public: + // \ref{optionalrref.ctor}, constructors + + constexpr optional() noexcept; + constexpr optional(nullopt_t) noexcept; + constexpr optional(const optional& rhs) noexcept = default; + constexpr optional(optional&& rhs) noexcept = default; + template + requires(!detail::is_optional>) + constexpr explicit(!is_convertible_v) optional(U&& u) noexcept; + template + constexpr explicit(!is_convertible_v) + optional(const optional& rhs) noexcept; + + // \ref{optionalrref.dtor}, destructor + constexpr ~optional() = default; + + // \ref{optionalrref.assign}, assignment + constexpr optional& operator=(nullopt_t) noexcept; + + constexpr optional& operator=(const optional& rhs) noexcept = default; + constexpr optional& operator=(optional&& rhs) noexcept = default; + + template + requires(!detail::is_optional>) + constexpr optional& operator=(U&& u); + + template + constexpr optional& operator=(const optional& rhs) noexcept; + + template + constexpr optional& operator=(optional&& rhs) = delete; + + template + requires(!detail::is_optional>) + constexpr optional& emplace(U&& u) noexcept; + + // \ref{optionalrref.swap}, swap + constexpr void swap(optional& rhs) noexcept; + + // \ref{optional.iterators}, iterator support + constexpr iterator begin() noexcept; + constexpr const_iterator begin() const noexcept; + constexpr iterator end() noexcept; + constexpr const_iterator end() const noexcept; + + // \ref{optionalrref.observe}, observers + constexpr T* operator->() const noexcept; + constexpr T&& operator*() const noexcept; + constexpr explicit operator bool() const noexcept; + constexpr bool has_value() const noexcept; + constexpr T&& value() const; + template + constexpr T value_or(U&& u) const; + + // \ref{optionalrref.monadic}, monadic operations + template + constexpr auto and_then(F&& f) const; + template + constexpr auto transform(F&& f) const -> optional>; + template + constexpr optional or_else(F&& f) const; + + // \ref{optional.mod}, modifiers + constexpr void reset() noexcept; + + private: + T* value_; // exposition only +}; + +// \rSec3[optionalrref.ctor]{Constructors} +template +constexpr optional::optional() noexcept : value_(nullptr) {} + +template +constexpr optional::optional(nullopt_t) noexcept : value_(nullptr) {} + +template +template + requires(!detail::is_optional>) +constexpr optional::optional(U&& u) noexcept : value_(addressof(u)) { + static_assert(is_constructible_v, U>, + "Must be able to bind U to T&&"); +} + +template +template +constexpr optional::optional(const optional& rhs) noexcept { + static_assert(is_constructible_v, U>, + "Must be able to bind U to T&&"); + if (rhs.has_value()) + value_ = to_address(rhs); + else + value_ = nullptr; +} + +// \rSec3[optionalrref.assign]{Assignment} +template +constexpr optional& optional::operator=(nullopt_t) noexcept { + value_ = nullptr; + return *this; +} + +template +template + requires(!detail::is_optional>) +constexpr optional& optional::operator=(U&& u) { + static_assert(is_constructible_v, U>, + "Must be able to bind U to T&&"); + value_ = addressof(u); + return *this; +} + +template +template +constexpr optional& +optional::operator=(const optional& rhs) noexcept { + static_assert(is_constructible_v, U>, + "Must be able to bind U to T&"); + if (rhs.has_value()) + value_ = to_address(rhs); + else + value_ = nullptr; + return *this; +} + +template +template + requires(!detail::is_optional>) +constexpr optional& optional::emplace(U&& u) noexcept { + return *this = std::forward(u); +} + +// \rSec3[optionalrref.swap]{Swap} + +template +constexpr void optional::swap(optional& rhs) noexcept { + std::swap(value_, rhs.value_); +} + +// \rSec3[optionalrref.iterators]{Iterator Support} +template +constexpr optional::iterator optional::begin() noexcept { + return iterator(has_value() ? value_ : nullptr); +}; + +template +constexpr optional::const_iterator optional::begin() const noexcept { + return const_iterator(has_value() ? value_ : nullptr); +}; + +template +constexpr optional::iterator optional::end() noexcept { + return begin() + has_value(); +} + +template +constexpr optional::const_iterator optional::end() const noexcept { + return begin() + has_value(); +} + +// \rSec3[optionalrref.observe]{Observers} +template +constexpr T* optional::operator->() const noexcept { + return value_; +} + +template +constexpr T&& optional::operator*() const noexcept { + return std::move(*value_); +} + +template +constexpr optional::operator bool() const noexcept { + return value_ != nullptr; +} +template +constexpr bool optional::has_value() const noexcept { + return value_ != nullptr; +} + +template +constexpr T&& optional::value() const { + if (has_value()) + return std::move(*value_); + throw bad_optional_access(); +} + +template +template +constexpr T optional::value_or(U&& u) const { + static_assert(is_copy_constructible_v, "T must be copy constructible"); + static_assert(is_convertible_v, + "Must be able to bind u to T"); + return has_value() ? std::move(*value_) : std::forward(u); +} + +// \rSec3[optionalrref.monadic]{Monadic operations} +template +template +constexpr auto optional::and_then(F&& f) const { + using U = invoke_result_t; + static_assert(detail::is_optional, "F must return an optional"); + return (has_value()) ? invoke(std::forward(f), std::move(*value_)) + : remove_cvref_t(); +} + +template +template +constexpr auto +optional::transform(F&& f) const -> optional> { + using U = invoke_result_t; + return (has_value()) + ? optional{invoke(std::forward(f), std::move(*value_))} + : optional{}; +} + +template +template +constexpr optional optional::or_else(F&& f) const { + using U = invoke_result_t; + static_assert(is_same_v, optional>); + return has_value() ? std::move(*value_) : std::forward(f)(); +} + +// \rSec3[optional.mod]{modifiers} +template +constexpr void optional::reset() noexcept { + value_ = nullptr; +}