From ae5c2aa9acce500e2ad94c6781843c02310dbab3 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 1 Nov 2019 20:47:07 +0900 Subject: Omittable: Add direct-list-initialising operator=; document See the change in connection.cpp for the example of usage. Also: removed static_asserts: the first one is provided by std::optional, and the second one is only relevant to edit(). --- lib/util.h | 50 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 9 deletions(-) (limited to 'lib/util.h') diff --git a/lib/util.h b/lib/util.h index 5be62382..902b4bfc 100644 --- a/lib/util.h +++ b/lib/util.h @@ -44,27 +44,59 @@ struct HashQ { template using UnorderedMap = std::unordered_map>; -inline constexpr auto none = std::nullopt; +constexpr auto none = std::nullopt; /** `std::optional` with tweaks * - * Due to tweaks, only works with default-constructible types. + * The tweaks are: + * - streamlined assignment (operator=)/emplace()ment of values that can be + * used to implicitly construct the underlying type, including + * direct-list-initialisation, e.g.: + * \code + * struct S { int a; char b; } + * Omittable o; + * o = { 1, 'a' }; // std::optional would require o = S { 1, 'a' } + * \endcode + * - entirely deleted value(). The technical reason is that Xcode 10 doesn't + * 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. + * - edit() to provide a safe and explicit lvalue accessor instead of those + * above. Requires the underlying type to be default-constructible. + * Allows chained initialisation of nested Omittables: + * \code + * struct Inner { int member = 10; Omittable innermost; }; + * struct Outer { int anotherMember = 10; Omittable inner; }; + * Omittable o; // = { 10, std::nullopt }; + * o.edit().inner.edit().innermost.emplace(42); + * \endcode + * - merge() - a soft version of operator= that only overwrites its first + * operand with the second one if the second one is not empty. */ template class Omittable : public std::optional { - static_assert(!std::is_reference::value, - "You cannot make an Omittable<> with a reference type"); - public: using base_type = std::optional; using value_type = std::decay_t; - static_assert(std::is_default_constructible_v, - "Omittable<> requires a default-constructible type"); using std::optional::optional; - // Overload emplace() to allow passing braced-init-lists (the standard - // emplace() does direct-initialisation but not direct-list-initialisation). + // Overload emplace() and operator=() to allow passing braced-init-lists + // (the standard emplace() does direct-initialisation but + // not direct-list-initialisation). + using base_type::operator=; + Omittable& operator=(const value_type& v) + { + base_type::operator=(v); + return *this; + } + Omittable& operator=(value_type&& v) + { + base_type::operator=(v); + return *this; + } using base_type::emplace; T& emplace(const T& val) { return base_type::emplace(val); } T& emplace(T&& val) { return base_type::emplace(std::move(val)); } -- cgit v1.2.3