diff --git a/framework/src/errors.h b/framework/src/errors.h --- a/framework/src/errors.h +++ b/framework/src/errors.h @@ -104,9 +104,13 @@ struct StorageBase { protected: - // Rule of 5 {{{ - StorageBase(const StorageBase &other) : mIsValue(other.mIsValue) + // To be able to define a copy constructor in a child class + StorageBase() {} + + // Rule of 5 (copy constructors defined in StorageCopyConstructor) {{{ + + StorageBase(StorageBase &&other) : mIsValue(other.mIsValue) { // This is a constructor, you have to construct object, not assign them // (hence the placement new) @@ -127,34 +131,13 @@ // // And so, the placement new allows us to call the constructor of // `Type` or `Error` instead of its assignment operator. - if (mIsValue) { - new (std::addressof(mValue)) Type(other.mValue); - } else { - new (std::addressof(mError)) Unexpected(other.mError); - } - } - - StorageBase(StorageBase &&other) : mIsValue(other.mIsValue) - { - // If you're thinking WTF, see the comment in the copy constructor above. if (mIsValue) { new (std::addressof(mValue)) Type(std::move(other.mValue)); } else { new (std::addressof(mError)) Unexpected(std::move(other.mError)); } } - constexpr StorageBase &operator=(const StorageBase &other) - { - mIsValue = other.mIsValue; - if (mIsValue) { - mValue = other.mValue; - } else { - mError = other.mError; - } - return *this; - } - constexpr StorageBase &operator=(StorageBase &&other) { this->~StorageBase(); @@ -215,13 +198,56 @@ bool mIsValue; }; +// Struct used to add the copy constructor / assignment only if both types are copy constructible +template ::value &&std::is_copy_constructible::value> +struct StorageCopyConstructor; + +template +struct StorageCopyConstructor : StorageBase +{ +protected: + using StorageBase::StorageBase; + + StorageCopyConstructor(const StorageCopyConstructor &other) : StorageBase() + { + // If you're thinking WTF, see the comment in the move constructor above. + this->mIsValue = other.mIsValue; + if (this->mIsValue) { + new (std::addressof(this->mValue)) Type(other.mValue); + } else { + new (std::addressof(this->mError)) Unexpected(other.mError); + } + } + + StorageCopyConstructor &operator=(const StorageCopyConstructor &other) + { + this->mIsValue = other.mIsValue; + if (this->mIsValue) { + this->mValue = other.mValue; + } else { + this->mError = other.mError; + } + return *this; + } + +}; + +template +struct StorageCopyConstructor : StorageBase +{ +protected: + using StorageBase::StorageBase; +}; + + // Write functions here when storage related, whether Type is void or not template -struct Storage : StorageBase +struct Storage : StorageCopyConstructor { protected: // Forward the construction to StorageBase - using StorageBase::StorageBase; + using StorageCopyConstructor::StorageCopyConstructor; }; // Write functions here when dev API related and when Type != void @@ -266,17 +292,17 @@ // Write functions here when dev API related and when Type == void template -struct ExpectedBase : detail::Storage +struct ExpectedBase : Storage { // Rewrite constructors for unexpected because Expected doesn't have direct access to it. template constexpr ExpectedBase(const Unexpected &error) - : detail::Storage(detail::tags::Unexpected{}, error) + : Storage(tags::Unexpected{}, error) { } template constexpr ExpectedBase(Unexpected &&error) - : detail::Storage(detail::tags::Unexpected{}, std::move(error)) + : Storage(tags::Unexpected{}, std::move(error)) { } };