diff --git a/mfbt/Maybe.h b/mfbt/Maybe.h index 4d26f7af9111..0bf6757c27c2 100644 --- a/mfbt/Maybe.h +++ b/mfbt/Maybe.h @@ -265,6 +265,11 @@ struct MaybeStorage { explicit MaybeStorage(const T& aVal) : mStorage{aVal}, mIsSome{true} {} explicit MaybeStorage(T&& aVal) : mStorage{std::move(aVal)}, mIsSome{true} {} + template + explicit MaybeStorage(Args&&... aArgs) : mIsSome{true} { + ::new (KnownNotNull, &mStorage.val) T(std::forward(aArgs)...); + } + // Copy and move operations are no-ops, since copying is moving is implemented // by Maybe_CopyMove_Enabler. @@ -299,6 +304,14 @@ struct MaybeStorage { : mStorage{aVal}, mIsSome{true} {} constexpr explicit MaybeStorage(T&& aVal) : mStorage{std::move(aVal)}, mIsSome{true} {} + + // XXX This should be constexpr, but then we can't use placement new. + // Delegating this to a Union constructor with the same signature doesn't + // build out of the box. + template + explicit MaybeStorage(Args&&... aArgs) : mIsSome{true} { + ::new (KnownNotNull, &mStorage.val) T(std::forward(aArgs)...); + } }; } // namespace detail @@ -350,9 +363,6 @@ constexpr Maybe Some(T&& aValue); * Boost. The most important differences between Maybe and std::optional are: * * - std::optional may be compared with T. We deliberately forbid that. - * - std::optional allows in-place construction without a separate call to - * |emplace()| by using a dummy |in_place_t| value to tag the appropriate - * constructor. * - std::optional has |valueOr()|, equivalent to Maybe's |valueOr()|, but * lacks corresponding methods for |refOr()| and |ptrOr()|. * - std::optional lacks |map()| and |apply()|, making it less suitable for @@ -389,6 +399,10 @@ class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS Maybe MOZ_ALLOW_TEMPORARY MOZ_IMPLICIT constexpr Maybe(Nothing) : Maybe{} {} + template + constexpr explicit Maybe(std::in_place_t, Args&&... aArgs) + : detail::MaybeStorage(std::forward(aArgs)...) {} + /** * Maybe can be copy-constructed from a Maybe if T is constructible from * a const U&. diff --git a/mfbt/tests/TestMaybe.cpp b/mfbt/tests/TestMaybe.cpp index 42929b1be69c..e9b28177c6b0 100644 --- a/mfbt/tests/TestMaybe.cpp +++ b/mfbt/tests/TestMaybe.cpp @@ -156,7 +156,7 @@ struct UncopyableUnmovableValue { ~UncopyableUnmovableValue() { --sUndestroyedObjects; } - Status GetStatus() { return mStatus; } + Status GetStatus() const { return mStatus; } private: UncopyableUnmovableValue(const UncopyableUnmovableValue& aOther) = delete; @@ -261,6 +261,14 @@ static bool TestBasicFeatures() { mayValue.reset(); MOZ_RELEASE_ASSERT(!mayValue); + { + // Check that Maybe(std::in_place, T1) calls the correct constructor. + const auto mayValueConstructed = Maybe(std::in_place, 1); + MOZ_RELEASE_ASSERT(mayValueConstructed); + MOZ_RELEASE_ASSERT(mayValueConstructed->GetStatus() == eWasConstructed); + MOZ_RELEASE_ASSERT(mayValueConstructed->GetTag() == 1); + } + // Check that Some() and Nothing() work. mayValue = Some(BasicValue(2)); MOZ_RELEASE_ASSERT(mayValue); @@ -497,6 +505,13 @@ static bool TestCopyAndMove() { MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects); { // Check that types that support neither moves or copies work. + { + const auto mayUncopyableUnmovableValueConstructed = + Maybe{std::in_place}; + MOZ_RELEASE_ASSERT(mayUncopyableUnmovableValueConstructed->GetStatus() == + eWasDefaultConstructed); + } + Maybe mayUncopyableUnmovableValue; mayUncopyableUnmovableValue.emplace(); MOZ_RELEASE_ASSERT(mayUncopyableUnmovableValue->GetStatus() ==