diff --git a/Source/Engine/Core/Types/Nullable.h b/Source/Engine/Core/Types/Nullable.h index f291eb0dc..fe6db7e4a 100644 --- a/Source/Engine/Core/Types/Nullable.h +++ b/Source/Engine/Core/Types/Nullable.h @@ -80,6 +80,7 @@ public: Nullable(Nullable&& other) noexcept { _hasValue = other._hasValue; + if (_hasValue) { new (&_value) T(MoveTemp(other._value)); // Placement new (move constructor) @@ -112,31 +113,35 @@ public: { KillOld(); - _hasValue = other._hasValue; - if (_hasValue) + if (other._hasValue) { new (&_value) T(other._value); // Placement new (copy constructor) } + _hasValue = other._hasValue; // Set the flag AFTER the value is copied. return *this; } auto operator=(Nullable&& other) noexcept -> Nullable& { + if (this == &other) + { + return *this; + } + KillOld(); - _hasValue = other._hasValue; if (_hasValue) { new (&_value) T(MoveTemp(other._value)); // Placement new (move constructor) other.Reset(); } + _hasValue = other._hasValue; // Set the flag AFTER the value is moved. return *this; } -public: /// /// Gets a value indicating whether the current NullableBase{T} object has a valid value of its underlying type. /// @@ -168,6 +173,16 @@ public: return _value; } + FORCE_INLINE const T& GetValueOr(const T& defaultValue) const + { + return _hasValue ? _value : defaultValue; + } + + FORCE_INLINE T GetValueOr(T&& defaultValue) const noexcept + { + return _hasValue ? _value : defaultValue; + } + /// /// Sets the wrapped value. /// @@ -180,7 +195,7 @@ public: } new (&_value) T(value); // Placement new (copy constructor) - _hasValue = true; + _hasValue = true; // Set the flag AFTER the value is copied. } /// @@ -192,7 +207,31 @@ public: KillOld(); new (&_value) T(MoveTemp(value)); // Placement new (move constructor) - _hasValue = true; + _hasValue = true; // Set the flag AFTER the value is moved. + } + + FORCE_INLINE bool TrySet(T&& value) noexcept + { + if (_hasValue) + { + return false; + } + + new (&_value) T(MoveTemp(value)); // Placement new (move constructor) + _hasValue = true; // Set the flag AFTER the value is moved. + return true; + } + + FORCE_INLINE bool TrySet(const T& value) + { + if (_hasValue) + { + return false; + } + + new (&_value) T(value); // Placement new (copy constructor) + _hasValue = true; // Set the flag AFTER the value is copied. + return true; } /// @@ -201,7 +240,7 @@ public: FORCE_INLINE void Reset() { KillOld(); - _hasValue = false; + _hasValue = false; // Reset the flag AFTER the value is (potentially) destructed. } /// @@ -211,11 +250,9 @@ public: { ASSERT(_hasValue); value = MoveTemp(_value); - _value.~T(); // Move is not destructive. - _hasValue = false; + Reset(); } -public: /// /// Indicates whether the current NullableBase{T} object is equal to a specified object. /// @@ -306,11 +343,27 @@ public: return _value == Value::True; } + FORCE_INLINE bool GetValueOr(const bool defaultValue) const noexcept + { + return _value == Value::Null ? defaultValue : _value == Value::True; + } + FORCE_INLINE void SetValue(const bool value) noexcept { _value = value ? Value::True : Value::False; } + FORCE_INLINE bool TrySet(const bool value) noexcept + { + if (_value != Value::Null) + { + return false; + } + + _value = value ? Value::True : Value::False; + return true; + } + FORCE_INLINE void Reset() noexcept { _value = Value::Null;