1 // Copyright 2022 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef BASE_TYPES_OPTIONAL_REF_H_ 6 #define BASE_TYPES_OPTIONAL_REF_H_ 7 8 #include <memory> 9 #include <optional> 10 #include <type_traits> 11 12 #include "base/check.h" 13 #include "base/memory/raw_ptr.h" 14 #include "third_party/abseil-cpp/absl/base/attributes.h" 15 #include "third_party/abseil-cpp/absl/types/optional.h" 16 17 namespace base { 18 19 // `optional_ref<T>` is similar to `absl::optional<T>`, except it does not own 20 // the underlying value. 21 // 22 // When passing an optional parameter, prefer `optional_ref` to `const 23 // absl::optional<T>&` as the latter often results in hidden copies due to 24 // implicit conversions, e.g. given the function: 25 // 26 // void TakesOptionalString(const absl::optional<std::string>& str); 27 // 28 // And a call to that looks like: 29 // 30 // std::string s = "Hello world!"; 31 // TakesOptionalString(s); 32 // 33 // This copies `s` into a temporary `absl::optional<std::string>` in order to 34 // call `TakesOptionalString()`. 35 // 36 // The C++ style guide recommends using `const T*` instead of `const 37 // absl::optional<T>&` when `T` would normally be passed by reference. However 38 // `const T*` is not always a good substitute because: 39 // 40 // - `const T*` disallows the use of temporaries, since it is not possible to 41 // take the address of a temporary. 42 // - additional boilerplate (e.g. `OptionalToPtr`) is required to pass an 43 // `absl::optional<T>` to a `const T*` function parameter. 44 // 45 // Like `span<T>`, mutability of `optional_ref<T>` is controlled by the template 46 // argument `T`; e.g. `optional_ref<const int>` only allows const access to the 47 // referenced `int` value. 48 // 49 // Thus, `optional_ref<const T>` can be constructed from: 50 // - `absl::nullopt` 51 // - `const T*` or `T*` 52 // - `const T&` or `T&` 53 // ` `const absl::optional<T>&` or `absl::optional<T>&` 54 // 55 // While `optional_ref<T>` can only be constructed from: 56 // - `absl::nullopt` 57 // - `T*` 58 // - `T&` 59 // - `absl::optional<T>&` 60 // 61 // Implicit conversions are disallowed, e.g. this will not compile: 62 // 63 // [](base::optional_ref<std::string> s) {}("Hello world!"); 64 // 65 // This restriction may be relaxed in the future if it proves too onerous. 66 // 67 // `optional_ref<T>` is lightweight and should be passed by value. It is copy 68 // constructible but not copy assignable, to reduce the risk of lifetime bugs. 69 template <typename T> 70 class optional_ref { 71 private: 72 // Disallowed because `absl::optional` (and `std::optional`) do not allow 73 // their template argument to be a reference type. 74 static_assert(!std::is_reference_v<T>, 75 "T must not be a reference type (use a pointer?)"); 76 77 // Both checks are important here, as: 78 // - optional_ref does not allow silent implicit conversions between types, 79 // so the decayed types must match exactly. 80 // - unless the types differ only in const qualification, and T is at least as 81 // const-qualified as the incoming type U. 82 template <typename U> 83 static constexpr bool IsCompatibleV = 84 std::is_same_v<std::decay_t<T>, std::decay_t<U>> && 85 std::is_convertible_v<U*, T*>; 86 87 public: 88 // Constructs an empty `optional_ref`. 89 constexpr optional_ref() = default; 90 // NOLINTNEXTLINE(google-explicit-constructor) optional_ref(absl::nullopt_t)91 constexpr optional_ref(absl::nullopt_t) {} 92 93 // Constructs an `optional_ref` from an `absl::optional`; the resulting 94 // `optional_ref` is empty iff `o` is empty. 95 // 96 // Note: when constructing from a const reference, `optional_ref`'s template 97 // argument must be const-qualified as well. 98 // Note 2: avoiding direct use of `T` prevents implicit conversions. 99 template <typename U> requires(std::is_const_v<T> && IsCompatibleV<U>)100 requires(std::is_const_v<T> && IsCompatibleV<U>) 101 // NOLINTNEXTLINE(google-explicit-constructor) 102 constexpr optional_ref( 103 const absl::optional<U>& o ABSL_ATTRIBUTE_LIFETIME_BOUND) 104 : ptr_(o ? &*o : nullptr) {} 105 template <typename U> requires(IsCompatibleV<U>)106 requires(IsCompatibleV<U>) 107 // NOLINTNEXTLINE(google-explicit-constructor) 108 constexpr optional_ref(absl::optional<U>& o ABSL_ATTRIBUTE_LIFETIME_BOUND) 109 : ptr_(o ? &*o : nullptr) {} 110 111 // Constructs an `optional_ref` from a pointer; the resulting `optional_ref` 112 // is empty iff `p` is null. 113 // 114 // Note: when constructing from a const pointer, `optional_ref`'s template 115 // argument must be const-qualified as well. 116 // Note 2: avoiding direct use of `T` prevents implicit conversions. 117 template <typename U> requires(IsCompatibleV<U>)118 requires(IsCompatibleV<U>) 119 // NOLINTNEXTLINE(google-explicit-constructor) 120 constexpr optional_ref(U* p ABSL_ATTRIBUTE_LIFETIME_BOUND) : ptr_(p) {} 121 122 // Constructs an `optional_ref` from a reference; the resulting `optional_ref` 123 // is never empty. 124 // 125 // Note: when constructing from a const reference, `optional_ref`'s template 126 // argument must be const-qualified as well. 127 // Note 2: avoiding direct use of `T` prevents implicit conversions. 128 template <typename U> requires(IsCompatibleV<const U>)129 requires(IsCompatibleV<const U>) 130 // NOLINTNEXTLINE(google-explicit-constructor) 131 constexpr optional_ref(const U& r ABSL_ATTRIBUTE_LIFETIME_BOUND) 132 : ptr_(std::addressof(r)) {} 133 template <typename U> requires(IsCompatibleV<U>)134 requires(IsCompatibleV<U>) 135 // NOLINTNEXTLINE(google-explicit-constructor) 136 constexpr optional_ref(U& r ABSL_ATTRIBUTE_LIFETIME_BOUND) 137 : ptr_(std::addressof(r)) {} 138 139 // An empty `optional_ref` must be constructed with `absl::nullopt`, not 140 // `nullptr`. Otherwise, `optional_ref<T*>` constructed with `nullptr` would 141 // be ambiguous: is it empty or is it engaged with a value of `nullptr`? 142 constexpr optional_ref(std::nullptr_t) = delete; 143 144 // Constructs a `optional_ref<const T>` from a `optional_ref<T>`. Conversions 145 // in the reverse direction are disallowed. 146 // NOLINTNEXTLINE(google-explicit-constructor) optional_ref(optional_ref<std::remove_const_t<T>> rhs)147 constexpr optional_ref(optional_ref<std::remove_const_t<T>> rhs) 148 requires(std::is_const_v<T>) 149 : ptr_(rhs.as_ptr()) {} 150 151 // Copy construction is allowed to make it possible to pass `optional_ref`s to 152 // another call. However, assignment is disallowed, as it makes it easy to 153 // violate lifetime bounds. Use `CopyAsOptional()` if an `optional_ref` needs 154 // to be persisted beyond the scope of a function call. 155 constexpr optional_ref(const optional_ref&) = default; 156 optional_ref& operator=(const optional_ref&) = delete; 157 158 // CHECKs if the `optional_ref` is empty. 159 constexpr T* operator->() const { 160 CHECK(ptr_); 161 return ptr_; 162 } 163 164 // CHECKs if the `optional_ref` is empty. 165 constexpr T& operator*() const { 166 CHECK(ptr_); 167 return *ptr_; 168 } 169 170 // Returns `true` iff the `optional_ref` is non-empty. has_value()171 constexpr bool has_value() const { return ptr_; } 172 173 // CHECKs if the `optional_ref` is empty. value()174 constexpr T& value() const { 175 CHECK(ptr_); 176 return *ptr_; 177 } 178 179 // Convenience method for turning an `optional_ref` into a pointer. as_ptr()180 constexpr T* as_ptr() const { return ptr_; } 181 182 // Convenience method for turning a non-owning `optional_ref` into an owning 183 // `absl::optional`. Incurs a copy; useful when saving an `optional_ref` 184 // function parameter as a field, et cetera. 185 template <typename U = std::decay_t<T>> requires(std::constructible_from<U,T>)186 requires(std::constructible_from<U, T>) 187 constexpr absl::optional<U> CopyAsOptional() const { 188 return ptr_ ? absl::optional<U>(*ptr_) : absl::nullopt; 189 } 190 191 private: 192 raw_ptr<T> const ptr_ = nullptr; 193 }; 194 195 template <typename T> 196 optional_ref(const T&) -> optional_ref<const T>; 197 template <typename T> 198 optional_ref(T&) -> optional_ref<T>; 199 200 template <typename T> 201 optional_ref(const absl::optional<T>&) -> optional_ref<const T>; 202 template <typename T> 203 optional_ref(absl::optional<T>&) -> optional_ref<T>; 204 205 template <typename T> 206 optional_ref(T*) -> optional_ref<T>; 207 208 template <typename T> 209 constexpr bool operator==(std::nullopt_t, optional_ref<T> x) { 210 return !x.has_value(); 211 } 212 213 template <typename T> 214 constexpr bool operator==(optional_ref<T> x, std::nullopt_t) { 215 return !x.has_value(); 216 } 217 218 } // namespace base 219 220 #endif // BASE_TYPES_OPTIONAL_REF_H_ 221