diff --git a/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp b/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp index 85b91bf..c2793ce 100644 --- a/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp +++ b/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp @@ -96,14 +96,14 @@ void TestOptional() TOptional TempF(0.0); TOptional TempG(TempA); TOptional TempH(TempD); - TOptional TempI(MakeOptional(0)); - TOptional TempJ(MakeOptional(Invalid)); + TOptional TempI(MakeOptional(0)); + TOptional TempJ(Invalid); TOptional TempK, TempL, TempM, TempN; TempK = TempA; TempL = TempD; - TempM = MakeOptional(0); - TempN = MakeOptional(Invalid); + TempM = MakeOptional(0); + TempN = Invalid; *TempL = 303; *TempM = 404; @@ -161,8 +161,8 @@ void TestOptional() TempZ = MakeOptional(); TempZ = FTracker(); - always_check(GetTypeHash(MakeOptional(114)) == GetTypeHash(MakeOptional(114))); - always_check(GetTypeHash(MakeOptional(114)) != GetTypeHash(MakeOptional(514))); + always_check(GetTypeHash(MakeOptional(114)) == GetTypeHash(MakeOptional(114))); + always_check(GetTypeHash(MakeOptional(114)) != GetTypeHash(MakeOptional(514))); } { @@ -182,6 +182,81 @@ void TestOptional() TOptional Temp(InPlace, { 0, 1, 2 }, 3); Temp.Emplace({ 0, 1, 2 }, 3); } + + { + int32 IntegerA = 0; + int32 IntegerB = 0; + + TOptional TempA; + TOptional TempB(Invalid); + TOptional TempC(IntegerA); + TOptional TempD(TempA); + TOptional TempE(TempC); + + TOptional TempF; + TOptional TempG(Invalid); + TOptional TempH(IntegerA); + TOptional TempI(TempA); + TOptional TempJ(TempC); + + TOptional TempK, TempL, TempM, TempN; + TempK = TempA; + TempL = TempE; + TempM = IntegerB; + TempN = Invalid; + + *TempL = 303; + *TempM = 404; + + always_check(!TempA); + always_check(!TempF.IsValid()); + + always_check(IntegerA == 303); + always_check(IntegerB == 404); + + always_check(TempH == 303); + always_check(TempM == 404); + + always_check(IntegerA < IntegerB); + + always_check(TempH < TempM); + + *TempC = 404; + + always_check(IntegerA == IntegerB); + + always_check(TempH == TempM); + + always_check(TempA.Get(IntegerA) == TempM); + + always_check(TempC.IsValid()); + + TempC.Reset(); + + always_check(!TempC.IsValid()); + } + + { + TOptional TempA = 303; + TOptional TempB = 404; + + TOptional TempC = TempA; + TOptional TempD = TempB; + + TOptional TempE = TempA; + TOptional TempF = TempB; + + TOptional TempG = TempC; + TOptional TempH = TempF; + + always_check(TempG == 303); + always_check(TempH == 404); + + always_check(TempG == TempC); + always_check(TempH == TempF); + always_check(TempE == TempG); + always_check(TempD == TempH); + } } void TestVariant() diff --git a/Redcraft.Utility/Source/Public/Templates/Optional.h b/Redcraft.Utility/Source/Public/Templates/Optional.h index dc77517..ae27998 100644 --- a/Redcraft.Utility/Source/Public/Templates/Optional.h +++ b/Redcraft.Utility/Source/Public/Templates/Optional.h @@ -11,7 +11,8 @@ NAMESPACE_REDCRAFT_BEGIN NAMESPACE_MODULE_BEGIN(Redcraft) NAMESPACE_MODULE_BEGIN(Utility) -template requires (CDestructible) +/** The class template manages an optional contained value or reference, i.e. a value or reference that may or may not be present. */ +template > requires (CDestructible>) class TOptional; NAMESPACE_PRIVATE_BEGIN @@ -39,12 +40,14 @@ NAMESPACE_PRIVATE_END template concept CTOptional = NAMESPACE_PRIVATE::TIsTOptional>::Value; /** The class template manages an optional contained value, i.e. a value that may or may not be present. */ -template requires (CDestructible) -class TOptional final +template requires (!CReference) +class TOptional final { public: - using ValueType = OptionalType; + using ValueType = T; + + static_assert(!CReference); /** Constructs an object that does not contain a value. */ FORCEINLINE constexpr TOptional() : bIsValid(false) { } @@ -52,80 +55,80 @@ public: /** Constructs an object that does not contain a value. */ FORCEINLINE constexpr TOptional(FInvalid) : TOptional() { } - /** Constructs an object with initial content an object, direct-initialized from Forward(InValue). */ - template requires (CConstructibleFrom) - && (!CSameAs, FInPlace>) && (!CSameAs>) - FORCEINLINE constexpr explicit (!CConvertibleTo) TOptional(T&& InValue) - : TOptional(InPlace, Forward(InValue)) + /** Constructs an object with initial content an object, direct-initialized from Forward(InValue). */ + template requires (CConstructibleFrom) + && (!CSameAs, FInPlace>) && (!CSameAs>) + FORCEINLINE constexpr explicit (!CConvertibleTo) TOptional(U&& InValue) + : TOptional(InPlace, Forward(InValue)) { } /** Constructs an object with initial content an object, direct-non-list-initialized from Forward(Args).... */ - template requires (CConstructibleFrom) + template requires (CConstructibleFrom) FORCEINLINE constexpr explicit TOptional(FInPlace, Ts&&... Args) : bIsValid(true) { - new (&Value) OptionalType(Forward(Args)...); + new (&Value) ValueType(Forward(Args)...); } /** Constructs an object with initial content an object, direct-non-list-initialized from IL, Forward(Args).... */ - template requires (CConstructibleFrom, Ts...>) - FORCEINLINE constexpr explicit TOptional(FInPlace, initializer_list IL, Ts&&... Args) + template requires (CConstructibleFrom, Ts...>) + FORCEINLINE constexpr explicit TOptional(FInPlace, initializer_list IL, Ts&&... Args) : bIsValid(true) { - new (&Value) OptionalType(IL, Forward(Args)...); + new (&Value) ValueType(IL, Forward(Args)...); } /** Copies content of other into a new instance. */ - FORCEINLINE constexpr TOptional(const TOptional& InValue) requires (CTriviallyCopyConstructible) = default; + FORCEINLINE constexpr TOptional(const TOptional& InValue) requires (CTriviallyCopyConstructible) = default; /** Copies content of other into a new instance. */ - FORCEINLINE constexpr TOptional(const TOptional& InValue) requires (CCopyConstructible && !CTriviallyCopyConstructible) + FORCEINLINE constexpr TOptional(const TOptional& InValue) requires (CCopyConstructible && !CTriviallyCopyConstructible) : bIsValid(InValue.IsValid()) { - if (InValue.IsValid()) new (&Value) OptionalType(InValue.GetValue()); + if (InValue.IsValid()) new (&Value) ValueType(InValue.GetValue()); } /** Moves content of other into a new instance. */ - FORCEINLINE constexpr TOptional(TOptional&& InValue) requires (CTriviallyMoveConstructible) = default; + FORCEINLINE constexpr TOptional(TOptional&& InValue) requires (CTriviallyMoveConstructible) = default; /** Moves content of other into a new instance. */ - FORCEINLINE constexpr TOptional(TOptional&& InValue) requires (CMoveConstructible && !CTriviallyMoveConstructible) + FORCEINLINE constexpr TOptional(TOptional&& InValue) requires (CMoveConstructible && !CTriviallyMoveConstructible) : bIsValid(InValue.IsValid()) { - if (InValue.IsValid()) new (&Value) OptionalType(MoveTemp(InValue.GetValue())); + if (InValue.IsValid()) new (&Value) ValueType(MoveTemp(InValue.GetValue())); } /** Converting copy constructor. */ - template requires (CConstructibleFrom && NAMESPACE_PRIVATE::CTOptionalAllowUnwrappable) - FORCEINLINE constexpr explicit (!CConvertibleTo) TOptional(const TOptional& InValue) + template requires (CConstructibleFrom && NAMESPACE_PRIVATE::CTOptionalAllowUnwrappable) + FORCEINLINE constexpr explicit (!CConvertibleTo) TOptional(const TOptional& InValue) : bIsValid(InValue.IsValid()) { - if (InValue.IsValid()) new (&Value) OptionalType(InValue.GetValue()); + if (InValue.IsValid()) new (&Value) ValueType(InValue.GetValue()); } /** Converting move constructor. */ - template requires (CConstructibleFrom && NAMESPACE_PRIVATE::CTOptionalAllowUnwrappable) - FORCEINLINE constexpr explicit (!CConvertibleTo) TOptional(TOptional&& InValue) + template requires (CConstructibleFrom && NAMESPACE_PRIVATE::CTOptionalAllowUnwrappable) + FORCEINLINE constexpr explicit (!CConvertibleTo) TOptional(TOptional&& InValue) : bIsValid(InValue.IsValid()) { - if (InValue.IsValid()) new (&Value) OptionalType(MoveTemp(InValue.GetValue())); + if (InValue.IsValid()) new (&Value) ValueType(MoveTemp(InValue.GetValue())); } /** Destroys the contained object, if any, as if by a call to Reset(). */ - FORCEINLINE constexpr ~TOptional() requires (CTriviallyDestructible) = default; + FORCEINLINE constexpr ~TOptional() requires (CTriviallyDestructible) = default; /** Destroys the contained object, if any, as if by a call to Reset(). */ - FORCEINLINE constexpr ~TOptional() requires (!CTriviallyDestructible) + FORCEINLINE constexpr ~TOptional() requires (!CTriviallyDestructible) { Reset(); } /** Assigns by copying the state of 'InValue'. */ - FORCEINLINE constexpr TOptional& operator=(const TOptional& InValue) requires (CTriviallyCopyConstructible && CTriviallyCopyAssignable) = default; + FORCEINLINE constexpr TOptional& operator=(const TOptional& InValue) requires (CTriviallyCopyConstructible && CTriviallyCopyAssignable) = default; /** Assigns by copying the state of 'InValue'. */ - constexpr TOptional& operator=(const TOptional& InValue) requires (CCopyConstructible && CCopyAssignable - && !CTriviallyCopyConstructible && !CTriviallyCopyAssignable) + constexpr TOptional& operator=(const TOptional& InValue) requires (CCopyConstructible && CCopyAssignable + && !CTriviallyCopyConstructible && !CTriviallyCopyAssignable) { if (&InValue == this) return *this; @@ -138,7 +141,7 @@ public: if (IsValid()) GetValue() = InValue.GetValue(); else { - new (&Value) OptionalType(InValue.GetValue()); + new (&Value) ValueType(InValue.GetValue()); bIsValid = true; } @@ -146,11 +149,11 @@ public: } /** Assigns by moving the state of 'InValue'. */ - FORCEINLINE constexpr TOptional& operator=(TOptional&& InValue) requires (CTriviallyMoveConstructible && CTriviallyMoveAssignable) = default; + FORCEINLINE constexpr TOptional& operator=(TOptional&& InValue) requires (CTriviallyMoveConstructible && CTriviallyMoveAssignable) = default; /** Assigns by moving the state of 'InValue'. */ - constexpr TOptional& operator=(TOptional&& InValue) requires (CMoveConstructible && CMoveAssignable - && !CTriviallyMoveConstructible && !CTriviallyMoveAssignable) + constexpr TOptional& operator=(TOptional&& InValue) requires (CMoveConstructible && CMoveAssignable + && !CTriviallyMoveConstructible && !CTriviallyMoveAssignable) { if (&InValue == this) return *this; @@ -163,7 +166,7 @@ public: if (IsValid()) GetValue() = MoveTemp(InValue.GetValue()); else { - new (&Value) OptionalType(MoveTemp(InValue.GetValue())); + new (&Value) ValueType(MoveTemp(InValue.GetValue())); bIsValid = true; } @@ -171,9 +174,9 @@ public: } /** Assigns by copying the state of 'InValue'. */ - template requires (CConstructibleFrom - && CAssignableFrom && NAMESPACE_PRIVATE::CTOptionalAllowUnwrappable) - constexpr TOptional& operator=(const TOptional& InValue) + template requires (CConstructibleFrom + && CAssignableFrom && NAMESPACE_PRIVATE::CTOptionalAllowUnwrappable) + constexpr TOptional& operator=(const TOptional& InValue) { if (!InValue.IsValid()) { @@ -184,7 +187,7 @@ public: if (IsValid()) GetValue() = InValue.GetValue(); else { - new (&Value) OptionalType(InValue.GetValue()); + new (&Value) ValueType(InValue.GetValue()); bIsValid = true; } @@ -192,9 +195,9 @@ public: } /** Assigns by moving the state of 'InValue'. */ - template requires (CConstructibleFrom - && CAssignableFrom && NAMESPACE_PRIVATE::CTOptionalAllowUnwrappable) - constexpr TOptional& operator=(TOptional&& InValue) + template requires (CConstructibleFrom + && CAssignableFrom && NAMESPACE_PRIVATE::CTOptionalAllowUnwrappable) + constexpr TOptional& operator=(TOptional&& InValue) { if (!InValue.IsValid()) { @@ -205,7 +208,7 @@ public: if (IsValid()) GetValue() = MoveTemp(InValue.GetValue()); else { - new (&Value) OptionalType(MoveTemp(InValue.GetValue())); + new (&Value) ValueType(MoveTemp(InValue.GetValue())); bIsValid = true; } @@ -213,13 +216,13 @@ public: } /** Assigns the value of 'InValue'. */ - template requires (CConstructibleFrom && CAssignableFrom) - FORCEINLINE constexpr TOptional& operator=(T&& InValue) + template requires (CConstructibleFrom && CAssignableFrom) + FORCEINLINE constexpr TOptional& operator=(U&& InValue) { - if (IsValid()) GetValue() = Forward(InValue); + if (IsValid()) GetValue() = Forward(InValue); else { - new (&Value) OptionalType(Forward(InValue)); + new (&Value) ValueType(Forward(InValue)); bIsValid = true; } @@ -227,8 +230,8 @@ public: } /** Check if the two optional are equivalent. */ - template requires (CWeaklyEqualityComparable) - NODISCARD friend FORCEINLINE constexpr bool operator==(const TOptional& LHS, const TOptional& RHS) + template requires (CWeaklyEqualityComparable) + NODISCARD friend FORCEINLINE constexpr bool operator==(const TOptional& LHS, const TOptional& RHS) { if (LHS.IsValid() != RHS.IsValid()) return false; if (LHS.IsValid() == false) return true; @@ -236,8 +239,8 @@ public: } /** Check the order relationship between two optional. */ - template requires (CSynthThreeWayComparable) - NODISCARD friend FORCEINLINE constexpr partial_ordering operator<=>(const TOptional& LHS, const TOptional& RHS) + template requires (CSynthThreeWayComparable) + NODISCARD friend FORCEINLINE constexpr partial_ordering operator<=>(const TOptional& LHS, const TOptional& RHS) { if (LHS.IsValid() != RHS.IsValid()) return partial_ordering::unordered; if (LHS.IsValid() == false) return partial_ordering::equivalent; @@ -245,15 +248,15 @@ public: } /** Check if the optional value is equivalent to 'InValue'. */ - template requires (!CTOptional && CWeaklyEqualityComparable) - NODISCARD FORCEINLINE constexpr bool operator==(const T& InValue) const& + template requires (!CTOptional && CWeaklyEqualityComparable) + NODISCARD FORCEINLINE constexpr bool operator==(const U& InValue) const& { return IsValid() ? GetValue() == InValue : false; } /** Check that the optional value is in ordered relationship with 'InValue'. */ - template requires (!CTOptional && CSynthThreeWayComparable) - NODISCARD FORCEINLINE constexpr partial_ordering operator<=>(const T& InValue) const& + template requires (!CTOptional && CSynthThreeWayComparable) + NODISCARD FORCEINLINE constexpr partial_ordering operator<=>(const U& InValue) const& { return IsValid() ? SynthThreeWayCompare(GetValue(), InValue) : partial_ordering::unordered; } @@ -270,12 +273,12 @@ public: * * @return A reference to the new object. */ - template requires (CConstructibleFrom) - FORCEINLINE constexpr OptionalType& Emplace(Ts&&... Args) + template requires (CConstructibleFrom) + FORCEINLINE constexpr T& Emplace(Ts&&... Args) { Reset(); - OptionalType* Result = new (&Value) OptionalType(Forward(Args)...); + T* Result = new (&Value) ValueType(Forward(Args)...); bIsValid = true; return *Result; @@ -290,12 +293,12 @@ public: * * @return A reference to the new object. */ - template requires (CConstructibleFrom, Ts...>) - FORCEINLINE constexpr OptionalType& Emplace(initializer_list IL, Ts&&... Args) + template requires (CConstructibleFrom, Ts...>) + FORCEINLINE constexpr T& Emplace(initializer_list IL, Ts&&... Args) { Reset(); - OptionalType* Result = new (&Value) OptionalType(IL, Forward(Args)...); + T* Result = new (&Value) ValueType(IL, Forward(Args)...); bIsValid = true; return *Result; @@ -306,24 +309,24 @@ public: NODISCARD FORCEINLINE constexpr explicit operator bool() const { return bIsValid; } /** @return The contained object. */ - NODISCARD FORCEINLINE constexpr OptionalType& GetValue() & { checkf(IsValid(), TEXT("It is an error to call GetValue() on an unset TOptional. Please either check IsValid() or use Get(DefaultValue) instead.")); return *reinterpret_cast< OptionalType*>(&Value); } - NODISCARD FORCEINLINE constexpr OptionalType&& GetValue() && { checkf(IsValid(), TEXT("It is an error to call GetValue() on an unset TOptional. Please either check IsValid() or use Get(DefaultValue) instead.")); return MoveTemp(*reinterpret_cast< OptionalType*>(&Value)); } - NODISCARD FORCEINLINE constexpr const OptionalType& GetValue() const& { checkf(IsValid(), TEXT("It is an error to call GetValue() on an unset TOptional. Please either check IsValid() or use Get(DefaultValue) instead.")); return *reinterpret_cast(&Value); } - NODISCARD FORCEINLINE constexpr const OptionalType&& GetValue() const&& { checkf(IsValid(), TEXT("It is an error to call GetValue() on an unset TOptional. Please either check IsValid() or use Get(DefaultValue) instead.")); return MoveTemp(*reinterpret_cast(&Value)); } + NODISCARD FORCEINLINE constexpr T& GetValue() & { checkf(IsValid(), TEXT("It is an error to call GetValue() on an unset TOptional. Please either check IsValid() or use Get(DefaultValue) instead.")); return *reinterpret_cast< T*>(&Value); } + NODISCARD FORCEINLINE constexpr T&& GetValue() && { checkf(IsValid(), TEXT("It is an error to call GetValue() on an unset TOptional. Please either check IsValid() or use Get(DefaultValue) instead.")); return MoveTemp(*reinterpret_cast< T*>(&Value)); } + NODISCARD FORCEINLINE constexpr const T& GetValue() const& { checkf(IsValid(), TEXT("It is an error to call GetValue() on an unset TOptional. Please either check IsValid() or use Get(DefaultValue) instead.")); return *reinterpret_cast(&Value); } + NODISCARD FORCEINLINE constexpr const T&& GetValue() const&& { checkf(IsValid(), TEXT("It is an error to call GetValue() on an unset TOptional. Please either check IsValid() or use Get(DefaultValue) instead.")); return MoveTemp(*reinterpret_cast(&Value)); } /** @return The pointer to the contained object. */ - NODISCARD FORCEINLINE constexpr const OptionalType* operator->() const { return &GetValue(); } - NODISCARD FORCEINLINE constexpr OptionalType* operator->() { return &GetValue(); } + NODISCARD FORCEINLINE constexpr const T* operator->() const { return &GetValue(); } + NODISCARD FORCEINLINE constexpr T* operator->() { return &GetValue(); } /** @return The contained object. */ - NODISCARD FORCEINLINE constexpr OptionalType& operator*() & { return GetValue(); } - NODISCARD FORCEINLINE constexpr OptionalType&& operator*() && { return GetValue(); } - NODISCARD FORCEINLINE constexpr const OptionalType& operator*() const& { return GetValue(); } - NODISCARD FORCEINLINE constexpr const OptionalType&& operator*() const&& { return GetValue(); } + NODISCARD FORCEINLINE constexpr T& operator*() & { return GetValue(); } + NODISCARD FORCEINLINE constexpr T&& operator*() && { return GetValue(); } + NODISCARD FORCEINLINE constexpr const T& operator*() const& { return GetValue(); } + NODISCARD FORCEINLINE constexpr const T&& operator*() const&& { return GetValue(); } /** @return The contained object when IsValid() returns true, 'DefaultValue' otherwise. */ - NODISCARD FORCEINLINE constexpr OptionalType& Get( OptionalType& DefaultValue) & { return IsValid() ? GetValue() : DefaultValue; } - NODISCARD FORCEINLINE constexpr const OptionalType& Get(const OptionalType& DefaultValue) const& { return IsValid() ? GetValue() : DefaultValue; } + NODISCARD FORCEINLINE constexpr T& Get( T& DefaultValue) & { return IsValid() ? GetValue() : DefaultValue; } + NODISCARD FORCEINLINE constexpr const T& Get(const T& DefaultValue) const& { return IsValid() ? GetValue() : DefaultValue; } /** If not empty, destroys the contained object. */ FORCEINLINE constexpr void Reset() @@ -332,20 +335,19 @@ public: { bIsValid = false; - typedef OptionalType DestructOptionalType; - ((OptionalType*)&Value)->DestructOptionalType::~DestructOptionalType(); + reinterpret_cast(&Value)->~ValueType(); } } /** Overloads the GetTypeHash algorithm for TOptional. */ - NODISCARD friend FORCEINLINE constexpr size_t GetTypeHash(const TOptional& A) requires (CHashable) + NODISCARD friend FORCEINLINE constexpr size_t GetTypeHash(const TOptional& A) requires (CHashable) { if (!A.IsValid()) return 2824517378; return GetTypeHash(A.GetValue()); } /** Overloads the Swap algorithm for TOptional. */ - friend constexpr void Swap(TOptional& A, TOptional& B) requires (CMoveConstructible && CSwappable) + friend constexpr void Swap(TOptional& A, TOptional& B) requires (CMoveConstructible && CSwappable) { if (!A.IsValid() && !B.IsValid()) return; @@ -367,21 +369,136 @@ public: private: - TAlignedStorage Value; + TAlignedStorage Value; bool bIsValid; }; +/** The class template manages an optional contained reference, i.e. a reference that may or may not be present. */ +template requires (CLValueReference) +class TOptional final +{ +public: + + using ValueType = TRemoveReference; + + static_assert(!CReference); + + /** Constructs an object that does not contain a reference. */ + FORCEINLINE constexpr TOptional() : Ptr(nullptr) { } + + /** Constructs an object that does not contain a reference. */ + FORCEINLINE constexpr TOptional(FInvalid) : TOptional() { } + + /** Constructs an object with initial content an object, direct-initialized from Forward(InValue). */ + template requires (CConstructibleFrom) + && (!CSameAs, FInPlace>) && (!CSameAs>) + FORCEINLINE constexpr explicit (!CConvertibleTo) TOptional(U&& InValue) + : Ptr(AddressOf(static_cast(Forward(InValue)))) + { } + + /** Copies content of other into a new instance. */ + FORCEINLINE constexpr TOptional(const TOptional&) = default; + FORCEINLINE constexpr TOptional(TOptional&&) = default; + + /** Converting constructor. */ + template requires (CConstructibleFrom && NAMESPACE_PRIVATE::CTOptionalAllowUnwrappable) + FORCEINLINE constexpr explicit (!CConvertibleTo) TOptional(TOptional InValue) + : Ptr(InValue.IsValid() ? AddressOf(static_cast(InValue.GetValue())) : nullptr) + { } + + /** Converting constructor. */ + template requires (!CConst && CConstructibleFrom && NAMESPACE_PRIVATE::CTOptionalAllowUnwrappable) + FORCEINLINE constexpr explicit (!CConvertibleTo) TOptional(TOptional& InValue) + : Ptr(InValue.IsValid() ? AddressOf(static_cast(InValue.GetValue())) : nullptr) + { } + + /** Converting constructor. */ + template requires (CConst && CConstructibleFrom && NAMESPACE_PRIVATE::CTOptionalAllowUnwrappable) + FORCEINLINE constexpr explicit (!CConvertibleTo) TOptional(const TOptional& InValue) + : Ptr(InValue.IsValid() ? AddressOf(static_cast(InValue.GetValue())) : nullptr) + { } + + /** Assigns by copying the state of 'InValue'. */ + FORCEINLINE constexpr TOptional& operator=(const TOptional&) = default; + FORCEINLINE constexpr TOptional& operator=(TOptional&&) = default; + + /** Destructor. */ + FORCEINLINE constexpr ~TOptional() = default; + + /** Check if the two optional are equivalent. */ + template requires (CWeaklyEqualityComparable) + NODISCARD friend FORCEINLINE constexpr bool operator==(TOptional LHS, TOptional RHS) + { + if (LHS.IsValid() != RHS.IsValid()) return false; + if (LHS.IsValid() == false) return true; + return *LHS == *RHS; + } + + /** Check the order relationship between two optional. */ + template requires (CSynthThreeWayComparable) + NODISCARD friend FORCEINLINE constexpr partial_ordering operator<=>(TOptional LHS, TOptional RHS) + { + if (LHS.IsValid() != RHS.IsValid()) return partial_ordering::unordered; + if (LHS.IsValid() == false) return partial_ordering::equivalent; + return SynthThreeWayCompare(*LHS, *RHS); + } + + /** Check if the optional reference is equivalent to 'InValue'. */ + template requires (!CTOptional && CWeaklyEqualityComparable) + NODISCARD FORCEINLINE constexpr bool operator==(const U& InValue) const& + { + return IsValid() ? GetValue() == InValue : false; + } + + /** Check that the optional reference is in ordered relationship with 'InValue'. */ + template requires (!CTOptional && CSynthThreeWayComparable) + NODISCARD FORCEINLINE constexpr partial_ordering operator<=>(const U& InValue) const& + { + return IsValid() ? SynthThreeWayCompare(GetValue(), InValue) : partial_ordering::unordered; + } + + /** @return true if instance does not contain a reference, otherwise false. */ + NODISCARD FORCEINLINE constexpr bool operator==(FInvalid) const& { return !IsValid(); } + + /** @return true if instance contains a reference, otherwise false. */ + NODISCARD FORCEINLINE constexpr bool IsValid() const { return Ptr != nullptr; } + NODISCARD FORCEINLINE constexpr explicit operator bool() const { return Ptr != nullptr; } + + /** @return The contained object. */ + NODISCARD FORCEINLINE constexpr T GetValue() const { checkf(IsValid(), TEXT("It is an error to call GetValue() on an unset TOptional. Please either check IsValid() or use Get(DefaultValue) instead.")); return *Ptr; } + + /** @return The pointer to the contained object. */ + NODISCARD FORCEINLINE constexpr auto operator->() const { return Ptr; } + + /** @return The contained object. */ + NODISCARD FORCEINLINE constexpr T operator*() const { return GetValue(); } + + /** @return The contained object when IsValid() returns true, 'DefaultValue' otherwise. */ + NODISCARD FORCEINLINE constexpr T Get(T DefaultValue) const { return IsValid() ? GetValue() : DefaultValue; } + + /** If not empty, destroys the contained object. */ + FORCEINLINE constexpr void Reset() + { + Ptr = nullptr; + } + + /** Overloads the GetTypeHash algorithm for TOptional. */ + NODISCARD friend FORCEINLINE constexpr size_t GetTypeHash(const TOptional& A) requires (CHashable) + { + if (!A.IsValid()) return 2824517378; + return GetTypeHash(A.GetValue()); + } + +private: + + ValueType* Ptr; + +}; + template TOptional(T) -> TOptional; -/** Creates an optional object that does not contain a value. */ -template requires (CDestructible) -NODISCARD FORCEINLINE constexpr TOptional MakeOptional(FInvalid) -{ - return TOptional(Invalid); -} - /** Creates an optional object from value. */ template requires (CDestructible> && CConstructibleFrom, T>) NODISCARD FORCEINLINE constexpr TOptional> MakeOptional(T&& InValue)