From 40b29069f55b551a67f4244c2b803b16e8287cd2 Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Sat, 1 Oct 2022 20:11:26 +0100 Subject: Relax Omittable::operator->/operator* The imposed limitations made mutation of Omittables pretty much impossible outside of rvalues. Honestly, it was pretty much the original intention - and it doesn't look right, in retrospect. (cherry picked from commit 7f886b34936f324662eb17afbb214d4800dcea03) --- lib/omittable.h | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/lib/omittable.h b/lib/omittable.h index b5efecf5..0718aaff 100644 --- a/lib/omittable.h +++ b/lib/omittable.h @@ -26,12 +26,17 @@ constexpr auto none = std::nullopt; //! \return Always an Omittable: if \p fn returns another type, lift() wraps //! it in an Omittable; if \p fn returns an Omittable, that return value //! (or none) is returned as is. -template -inline auto lift(FnT&& fn, MaybeTs&&... args) +template +inline auto lift(FnT&& fn, ArgTs&&... args) { - return (... && bool(args)) - ? Omittable(std::invoke(std::forward(fn), *args...)) - : none; + if constexpr (std::is_void_v(fn), + *args...))>) { + if ((... && bool(args))) + std::invoke(std::forward(fn), *args...); + } else + return (... && bool(args)) + ? Omittable(std::invoke(std::forward(fn), *args...)) + : none; } /** `std::optional` with tweaks @@ -49,10 +54,8 @@ inline auto lift(FnT&& fn, MaybeTs&&... args) * have it; but besides that, value_or() or (after explicit checking) * `operator*()`/`operator->()` are better alternatives within Quotient * that doesn't practice throwing exceptions (as doesn't most of Qt). - * - disabled non-const lvalue operator*() and operator->(), as it's too easy - * to inadvertently cause a value change through them. - * - ensure() to provide a safe and explicit lvalue accessor instead of - * those above. Allows chained initialisation of nested Omittables: + * - ensure() to provide a safer lvalue accessor instead of operator* or + * operator->. Allows chained initialisation of nested Omittables: * \code * struct Inner { int member = 10; Omittable innermost; }; * struct Outer { int anotherMember = 10; Omittable inner; }; @@ -95,7 +98,7 @@ public: // with operator-> or operator* // The technical reason is that Xcode 10 has incomplete std::optional // that has no value(); but using value() may also mean that you rely - // on the optional throwing an exception (which is not assumed practice + // on the optional throwing an exception (which is not an assumed practice // throughout Quotient) or that you spend unnecessary CPU cycles on // an extraneous has_value() check. auto& value() = delete; @@ -131,15 +134,6 @@ public: return true; } - // Hide non-const lvalue operator-> and operator* as these are - // a bit too surprising: value() & doesn't lazy-create an object; - // and it's too easy to inadvertently change the underlying value. - - const value_type* operator->() const& { return base_type::operator->(); } - value_type* operator->() && { return base_type::operator->(); } - const value_type& operator*() const& { return base_type::operator*(); } - value_type& operator*() && { return base_type::operator*(); } - // The below is inspired by the proposed std::optional monadic operations // (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0798r6.html). @@ -162,7 +156,7 @@ public: //! returning Omittable, T2 is any supported type //! \sa then_or, transform template - auto then(FnT&& fn) const& + auto then(FnT&& fn) const { return lift(std::forward(fn), *this); } @@ -171,7 +165,7 @@ public: //! //! This is an rvalue overload for then(). template - auto then(FnT&& fn) && + auto then(FnT&& fn) { return lift(std::forward(fn), *this); } @@ -184,7 +178,7 @@ public: //! an operation on an Omittable without having to deal with another //! Omittable afterwards. template - auto then_or(FnT&& fn, FallbackT&& fallback) const& + auto then_or(FnT&& fn, FallbackT&& fallback) const { return then(std::forward(fn)) .value_or(std::forward(fallback)); @@ -194,7 +188,7 @@ public: //! //! This is an overload for functions that accept rvalue template - auto then_or(FnT&& fn, FallbackT&& fallback) && + auto then_or(FnT&& fn, FallbackT&& fallback) { return then(std::forward(fn)) .value_or(std::forward(fallback)); -- cgit v1.2.3