Issue #2255 - Add support for Maybe<T&> https://bugzilla.mozilla.org/show_bug.cgi?id=1620568 Make Maybe<T>::emplace() work when T is const https://bugzilla.mozilla.org/show_bug.cgi?id=1335780

This commit is contained in:
Brian Smith 2023-07-12 14:24:28 -05:00 • committed by roytam1
commit efde4d468e

View file

@ -83,7 +83,15 @@ template<class T>
class Maybe class Maybe
{ {
bool mIsSome; bool mIsSome;
AlignedStorage2<T> mStorage;
// To support |Maybe<const Type>| we give |mStorage| the type |T| with any
// const-ness removed. That allows us to |emplace()| an object into
// |mStorage|. Since we treat the contained object as having type |T|
// everywhere else (both internally, and when exposed via public methods) the
// contained object is still treated as const once stored since |const| is
// part of |T|'s type signature.
typedef typename RemoveCV<T>::Type StorageType;
AlignedStorage2<StorageType> mStorage;
public: public:
typedef T ValueType; typedef T ValueType;
@ -453,6 +461,72 @@ public:
} }
}; };
template <typename T>
class Maybe<T&> {
public:
constexpr Maybe() = default;
constexpr MOZ_IMPLICIT Maybe(Nothing) {}
void emplace(T& aRef) { mValue = &aRef; }
/* Methods that check whether this Maybe contains a value */
explicit operator bool() const { return isSome(); }
bool isSome() const { return mValue; }
bool isNothing() const { return !mValue; }
T& ref() const {
MOZ_DIAGNOSTIC_ASSERT(isSome());
return *mValue;
}
T* operator->() const { return &ref(); }
T& operator*() const { return ref(); }
// Deliberately not defining value and ptr accessors, as these may be
// confusing on a reference-typed Maybe.
// XXX Should we define refOr?
void reset() { mValue = nullptr; }
template <typename Func>
Maybe& apply(Func&& aFunc) {
if (isSome()) {
std::forward<Func>(aFunc)(ref());
}
return *this;
}
template <typename Func>
const Maybe& apply(Func&& aFunc) const {
if (isSome()) {
std::forward<Func>(aFunc)(ref());
}
return *this;
}
template <typename Func>
auto map(Func&& aFunc) {
Maybe<decltype(std::forward<Func>(aFunc)(ref()))> val;
if (isSome()) {
val.emplace(std::forward<Func>(aFunc)(ref()));
}
return val;
}
template <typename Func>
auto map(Func&& aFunc) const {
Maybe<decltype(std::forward<Func>(aFunc)(ref()))> val;
if (isSome()) {
val.emplace(std::forward<Func>(aFunc)(ref()));
}
return val;
}
private:
T* mValue = nullptr;
};
/* /*
* Some() creates a Maybe<T> value containing the provided T value. If T has a * Some() creates a Maybe<T> value containing the provided T value. If T has a
* move constructor, it's used to make this as efficient as possible. * move constructor, it's used to make this as efficient as possible.
@ -474,6 +548,13 @@ Some(T&& aValue)
return value; return value;
} }
template <typename T>
Maybe<T&> SomeRef(T& aValue) {
Maybe<T&> value;
value.emplace(aValue);
return value;
}
template<typename T> template<typename T>
Maybe<typename RemoveCV<typename RemoveReference<T>::Type>::Type> Maybe<typename RemoveCV<typename RemoveReference<T>::Type>::Type>
ToMaybe(T* aPtr) ToMaybe(T* aPtr)
@ -492,6 +573,9 @@ ToMaybe(T* aPtr)
template<typename T> bool template<typename T> bool
operator==(const Maybe<T>& aLHS, const Maybe<T>& aRHS) operator==(const Maybe<T>& aLHS, const Maybe<T>& aRHS)
{ {
static_assert(!std::is_reference<T>::value,
"operator== is not defined for Maybe<T&>, compare values or "
"addresses explicitly instead");
if (aLHS.isNothing() != aRHS.isNothing()) { if (aLHS.isNothing() != aRHS.isNothing()) {
return false; return false;
} }