From defa7e1b9e16c6276e17a39b61d019c1a116472b Mon Sep 17 00:00:00 2001 From: Steven Le Rouzic Date: Fri, 22 Nov 2024 18:15:40 +0100 Subject: Use niche in option --- asl/option.hpp | 176 ++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 124 insertions(+), 52 deletions(-) (limited to 'asl/option.hpp') diff --git a/asl/option.hpp b/asl/option.hpp index 5f53ad8..6b528b9 100644 --- a/asl/option.hpp +++ b/asl/option.hpp @@ -4,6 +4,7 @@ #include "asl/meta.hpp" #include "asl/maybe_uninit.hpp" #include "asl/functional.hpp" +#include "asl/annotations.hpp" namespace asl { @@ -11,7 +12,6 @@ namespace asl struct nullopt_t {}; static constexpr nullopt_t nullopt{}; -// @Todo(option) Niche // @Todo(option) Reference // @Todo(option) Function // @Todo(option) Arrays @@ -71,31 +71,42 @@ class option trivially_move_assignable && trivially_destructible; - using Storage = select_t>; + static constexpr bool kHasNiche = has_niche; + + static constexpr bool kHasInlinePayload = kIsTrivial || kHasNiche; + + using Storage = select_t>; + using HasValueMarker = select_t; Storage m_payload{}; - bool m_has_value = false; + ASL_NO_UNIQUE_ADDRESS HasValueMarker m_has_value; template - constexpr void construct(Args&&... args) & + constexpr void construct(Args&&... args) { - ASL_ASSERT(!m_has_value); - m_has_value = true; - if constexpr (kIsTrivial) + ASL_ASSERT(!has_value()); + + if constexpr (kHasInlinePayload) { - new(&m_payload) T(ASL_FWD(args)...); + new (&m_payload) T(ASL_FWD(args)...); } else { m_payload.init_unsafe(ASL_FWD(args)...); } + + if constexpr (!kHasNiche) + { + m_has_value = true; + } } - template - constexpr void assign(Arg&& arg) & + template + constexpr void assign(U&& arg) { - ASL_ASSERT(m_has_value); - if constexpr (kIsTrivial) + ASL_ASSERT(has_value()); + + if constexpr (kHasInlinePayload) { m_payload = ASL_FWD(arg); } @@ -108,29 +119,69 @@ class option public: using type = T; - constexpr option() = default; - constexpr option(nullopt_t) {} // NOLINT(*-explicit-conversions) + constexpr option() : option(nullopt) {} + + // NOLINTNEXTLINE(*-explicit-conversions) + constexpr option(nullopt_t) requires (!kHasNiche) + : m_has_value{false} + , m_payload{} + {} + + // NOLINTNEXTLINE(*-explicit-conversions) + constexpr option(nullopt_t) requires kHasNiche + : m_payload{niche{}} + {} template constexpr explicit (!convertible_from) option(U&& value) requires ( + kHasNiche && constructible_from && - !same_as, option> + !same_as, option> && + !is_niche + ) + : m_payload(ASL_FWD(value)) + {} + + template + constexpr explicit (!convertible_from) + option(U&& value) + requires ( + kHasInlinePayload && + !kHasNiche && + constructible_from && + !same_as, option> && + !is_niche + ) + : m_payload(ASL_FWD(value)) + , m_has_value{true} + {} + + template + constexpr explicit (!convertible_from) + option(U&& value) + requires ( + !kHasInlinePayload && + constructible_from && + !same_as, option> && + !is_niche ) + : option() { construct(ASL_FWD(value)); } constexpr option(const option& other) - requires copy_constructible && kIsTrivial = default; + requires copy_constructible && kHasInlinePayload = default; constexpr option(const option& other) - requires copy_constructible && (!kIsTrivial) + requires copy_constructible && (!kHasInlinePayload) + : option() { - if (other.m_has_value) + if (other.has_value()) { - construct(other.m_payload.as_init_unsafe()); + construct(other.value()); } } @@ -138,17 +189,21 @@ public: requires (!copy_constructible) = delete; constexpr option(option&& other) - requires move_constructible && kIsTrivial = default; + requires move_constructible && kHasInlinePayload = default; constexpr option(option&& other) - requires move_constructible && (!kIsTrivial) + requires move_constructible && (!kHasInlinePayload) + : option() { - if (other.m_has_value) + if (other.has_value()) { - construct(ASL_MOVE(other.m_payload.as_init_unsafe())); + construct(ASL_MOVE(other.value())); } } + constexpr option(option&& other) + requires (!move_constructible) = delete; + template constexpr explicit (!convertible_from) option(const option& other) @@ -156,6 +211,7 @@ public: constructible_from && !option_internal::convertible_constructible_from_option ) + : option() { if (other.has_value()) { @@ -170,10 +226,11 @@ public: constructible_from && !option_internal::convertible_constructible_from_option ) + : option() { if (other.has_value()) { - construct(ASL_MOVE(other).value()); + construct(ASL_MOVE(other.value())); } } @@ -191,7 +248,7 @@ public: !same_as, option> ) { - if (m_has_value) + if (has_value()) { assign(ASL_FWD(value)); } @@ -204,19 +261,19 @@ public: } constexpr option& operator=(const option& other) & - requires (!copy_assignable || copy_constructible) = delete; + requires (!copy_assignable || !copy_constructible) = delete; constexpr option& operator=(const option& other) & - requires copy_assignable && copy_constructible && kIsTrivial = default; + requires copy_assignable && copy_constructible && kHasInlinePayload = default; constexpr option& operator=(const option& other) & - requires copy_assignable && copy_constructible && (!kIsTrivial) + requires copy_assignable && copy_constructible && (!kHasInlinePayload) { if (&other == this) { return *this; } - if (other.m_has_value) + if (other.has_value()) { - if (m_has_value) + if (has_value()) { assign(other.m_payload.as_init_unsafe()); } @@ -225,7 +282,7 @@ public: construct(other.m_payload.as_init_unsafe()); } } - else + else if (has_value()) { reset(); } @@ -234,16 +291,16 @@ public: } constexpr option& operator=(option&& other) & - requires move_assignable && move_constructible && kIsTrivial = default; + requires move_assignable && move_constructible && kHasInlinePayload = default; constexpr option& operator=(option&& other) & - requires move_assignable && move_constructible && (!kIsTrivial) + requires move_assignable && move_constructible && (!kHasInlinePayload) { if (&other == this) { return *this; } - if (other.m_has_value) + if (other.has_value()) { - if (m_has_value) + if (has_value()) { assign(ASL_MOVE(other.m_payload.as_init_unsafe())); } @@ -252,7 +309,7 @@ public: construct(ASL_MOVE(other.m_payload.as_init_unsafe())); } } - else + else if (has_value()) { reset(); } @@ -270,7 +327,7 @@ public: { if (other.has_value()) { - if (m_has_value) + if (has_value()) { assign(other.value()); } @@ -279,7 +336,7 @@ public: construct(other.value()); } } - else + else if (has_value()) { reset(); } @@ -297,7 +354,7 @@ public: { if (other.has_value()) { - if (m_has_value) + if (has_value()) { assign(ASL_MOVE(other).value()); } @@ -306,7 +363,7 @@ public: construct(ASL_MOVE(other).value()); } } - else + else if (has_value()) { reset(); } @@ -322,23 +379,38 @@ public: constexpr void reset() { - if constexpr (kIsTrivial) + if (!has_value()) { return; } + + if constexpr (kHasNiche) { - m_has_value = false; + m_payload = T(niche{}); } - else if (m_has_value) + else { - m_payload.uninit_unsafe(); m_has_value = false; + if constexpr (!kHasInlinePayload) + { + m_payload.uninit_unsafe(); + } } } - constexpr bool has_value() const { return m_has_value; } + constexpr bool has_value() const + { + if constexpr (kHasNiche) + { + return m_payload != niche{}; + } + else + { + return m_has_value; + } + } constexpr T&& value() && { - ASL_ASSERT_RELEASE(m_has_value); - if constexpr (kIsTrivial) + ASL_ASSERT_RELEASE(has_value()); + if constexpr (kHasInlinePayload) { return ASL_MOVE(m_payload); } @@ -350,8 +422,8 @@ public: constexpr T& value() & { - ASL_ASSERT_RELEASE(m_has_value); - if constexpr (kIsTrivial) + ASL_ASSERT_RELEASE(has_value()); + if constexpr (kHasInlinePayload) { return m_payload; } @@ -363,8 +435,8 @@ public: constexpr const T& value() const& { - ASL_ASSERT_RELEASE(m_has_value); - if constexpr (kIsTrivial) + ASL_ASSERT_RELEASE(has_value()); + if constexpr (kHasInlinePayload) { return m_payload; } @@ -392,7 +464,7 @@ public: constexpr T& emplace(Args&&... args) & requires constructible_from { - if (m_has_value) { reset(); } + if (has_value()) { reset(); } construct(ASL_FWD(args)...); return value(); } -- cgit