| OLD | NEW |
| 1 /* | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 * Copyright 2015 The WebRTC Project Authors. All rights reserved. | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 * | 3 // found in the LICENSE file. |
| 4 * Use of this source code is governed by a BSD-style license | |
| 5 * that can be found in the LICENSE file in the root of the source | |
| 6 * tree. An additional intellectual property rights grant can be found | |
| 7 * in the file PATENTS. All contributing project authors may | |
| 8 * be found in the AUTHORS file in the root of the source tree. | |
| 9 */ | |
| 10 | 4 |
| 11 #ifndef WEBRTC_BASE_OPTIONAL_H_ | 5 #ifndef WEBRTC_BASE_OPTIONAL_H_ |
| 12 #define WEBRTC_BASE_OPTIONAL_H_ | 6 #define WEBRTC_BASE_OPTIONAL_H_ |
| 13 | 7 |
| 14 #include <algorithm> | 8 #include <type_traits> |
| 15 #include <memory> | |
| 16 #include <utility> | |
| 17 | 9 |
| 18 #include "webrtc/base/checks.h" | 10 #include "webrtc/base/checks.h" |
| 11 #include "webrtc/base/template_util.h" |
| 19 | 12 |
| 20 namespace rtc { | 13 namespace rtc { |
| 21 | 14 |
| 22 // Simple std::optional-wannabe. It either contains a T or not. | 15 // Specification: |
| 23 // | 16 // http://en.cppreference.com/w/cpp/utility/optional/in_place_t |
| 24 // A moved-from Optional<T> may only be destroyed, and assigned to if T allows | 17 struct in_place_t {}; |
| 25 // being assigned to after having been moved from. Specifically, you may not | 18 |
| 26 // assume that it just doesn't contain a value anymore. | 19 // Specification: |
| 27 // | 20 // http://en.cppreference.com/w/cpp/utility/optional/nullopt_t |
| 28 // Examples of good places to use Optional: | 21 struct nullopt_t { |
| 29 // | 22 constexpr explicit nullopt_t(int) {} |
| 30 // - As a class or struct member, when the member doesn't always have a value: | 23 }; |
| 31 // struct Prisoner { | 24 |
| 32 // std::string name; | 25 // Specification: |
| 33 // Optional<int> cell_number; // Empty if not currently incarcerated. | 26 // http://en.cppreference.com/w/cpp/utility/optional/in_place |
| 34 // }; | 27 constexpr in_place_t in_place = {}; |
| 35 // | 28 |
| 36 // - As a return value for functions that may fail to return a value on all | 29 // Specification: |
| 37 // allowed inputs. For example, a function that searches an array might | 30 // http://en.cppreference.com/w/cpp/utility/optional/nullopt |
| 38 // return an Optional<size_t> (the index where it found the element, or | 31 constexpr nullopt_t nullopt(0); |
| 39 // nothing if it didn't find it); and a function that parses numbers might | 32 |
| 40 // return Optional<double> (the parsed number, or nothing if parsing failed). | 33 namespace internal { |
| 41 // | 34 |
| 42 // Examples of bad places to use Optional: | 35 template <typename T, bool = rtc::is_trivially_destructible<T>::value> |
| 43 // | 36 struct OptionalStorage { |
| 44 // - As a return value for functions that may fail because of disallowed | 37 OptionalStorage() : empty_('\0') {}; |
| 45 // inputs. For example, a string length function should not return | 38 // When T is not trivially destructible we must call its |
| 46 // Optional<size_t> so that it can return nothing in case the caller passed | 39 // destructor before deallocating its memory. |
| 47 // it a null pointer; the function should probably use RTC_[D]CHECK instead, | 40 ~OptionalStorage() { |
| 48 // and return plain size_t. | 41 if (!is_null_) |
| 49 // | |
| 50 // - As a return value for functions that may fail to return a value on all | |
| 51 // allowed inputs, but need to tell the caller what went wrong. Returning | |
| 52 // Optional<double> when parsing a single number as in the example above | |
| 53 // might make sense, but any larger parse job is probably going to need to | |
| 54 // tell the caller what the problem was, not just that there was one. | |
| 55 // | |
| 56 // TODO(kwiberg): Get rid of this class when the standard library has | |
| 57 // std::optional (and we're allowed to use it). | |
| 58 template <typename T> | |
| 59 class Optional final { | |
| 60 public: | |
| 61 // Construct an empty Optional. | |
| 62 Optional() : has_value_(false) {} | |
| 63 | |
| 64 // Construct an Optional that contains a value. | |
| 65 explicit Optional(const T& value) : has_value_(true) { | |
| 66 new (&value_) T(value); | |
| 67 } | |
| 68 explicit Optional(T&& value) : has_value_(true) { | |
| 69 new (&value_) T(std::move(value)); | |
| 70 } | |
| 71 | |
| 72 // Copy constructor: copies the value from m if it has one. | |
| 73 Optional(const Optional& m) : has_value_(m.has_value_) { | |
| 74 if (has_value_) | |
| 75 new (&value_) T(m.value_); | |
| 76 } | |
| 77 | |
| 78 // Move constructor: if m has a value, moves the value from m, leaving m | |
| 79 // still in a state where it has a value, but a moved-from one (the | |
| 80 // properties of which depends on T; the only general guarantee is that we | |
| 81 // can destroy m). | |
| 82 Optional(Optional&& m) : has_value_(m.has_value_) { | |
| 83 if (has_value_) | |
| 84 new (&value_) T(std::move(m.value_)); | |
| 85 } | |
| 86 | |
| 87 ~Optional() { | |
| 88 if (has_value_) | |
| 89 value_.~T(); | 42 value_.~T(); |
| 90 } | 43 } |
| 91 | 44 |
| 92 // Copy assignment. Uses T's copy assignment if both sides have a value, T's | 45 bool is_null_ = true; |
| 93 // copy constructor if only the right-hand side has a value. | |
| 94 Optional& operator=(const Optional& m) { | |
| 95 if (m.has_value_) { | |
| 96 if (has_value_) { | |
| 97 value_ = m.value_; // T's copy assignment. | |
| 98 } else { | |
| 99 new (&value_) T(m.value_); // T's copy constructor. | |
| 100 has_value_ = true; | |
| 101 } | |
| 102 } else if (has_value_) { | |
| 103 value_.~T(); | |
| 104 has_value_ = false; | |
| 105 } | |
| 106 return *this; | |
| 107 } | |
| 108 | |
| 109 // Move assignment. Uses T's move assignment if both sides have a value, T's | |
| 110 // move constructor if only the right-hand side has a value. The state of m | |
| 111 // after it's been moved from is as for the move constructor. | |
| 112 Optional& operator=(Optional&& m) { | |
| 113 if (m.has_value_) { | |
| 114 if (has_value_) { | |
| 115 value_ = std::move(m.value_); // T's move assignment. | |
| 116 } else { | |
| 117 new (&value_) T(std::move(m.value_)); // T's move constructor. | |
| 118 has_value_ = true; | |
| 119 } | |
| 120 } else if (has_value_) { | |
| 121 value_.~T(); | |
| 122 has_value_ = false; | |
| 123 } | |
| 124 return *this; | |
| 125 } | |
| 126 | |
| 127 // Swap the values if both m1 and m2 have values; move the value if only one | |
| 128 // of them has one. | |
| 129 friend void swap(Optional& m1, Optional& m2) { | |
| 130 if (m1.has_value_) { | |
| 131 if (m2.has_value_) { | |
| 132 // Both have values: swap. | |
| 133 using std::swap; | |
| 134 swap(m1.value_, m2.value_); | |
| 135 } else { | |
| 136 // Only m1 has a value: move it to m2. | |
| 137 new (&m2.value_) T(std::move(m1.value_)); | |
| 138 m1.value_.~T(); // Destroy the moved-from value. | |
| 139 m1.has_value_ = false; | |
| 140 m2.has_value_ = true; | |
| 141 } | |
| 142 } else if (m2.has_value_) { | |
| 143 // Only m2 has a value: move it to m1. | |
| 144 new (&m1.value_) T(std::move(m2.value_)); | |
| 145 m2.value_.~T(); // Destroy the moved-from value. | |
| 146 m1.has_value_ = true; | |
| 147 m2.has_value_ = false; | |
| 148 } | |
| 149 } | |
| 150 | |
| 151 // Conversion to bool to test if we have a value. | |
| 152 explicit operator bool() const { return has_value_; } | |
| 153 | |
| 154 // Dereferencing. Only allowed if we have a value. | |
| 155 const T* operator->() const { | |
| 156 RTC_DCHECK(has_value_); | |
| 157 return &value_; | |
| 158 } | |
| 159 T* operator->() { | |
| 160 RTC_DCHECK(has_value_); | |
| 161 return &value_; | |
| 162 } | |
| 163 const T& operator*() const { | |
| 164 RTC_DCHECK(has_value_); | |
| 165 return value_; | |
| 166 } | |
| 167 T& operator*() { | |
| 168 RTC_DCHECK(has_value_); | |
| 169 return value_; | |
| 170 } | |
| 171 | |
| 172 // Dereference with a default value in case we don't have a value. | |
| 173 const T& value_or(const T& default_val) const { | |
| 174 return has_value_ ? value_ : default_val; | |
| 175 } | |
| 176 | |
| 177 // Equality tests. Two Optionals are equal if they contain equivalent values, | |
| 178 // or | |
| 179 // if they're both empty. | |
| 180 friend bool operator==(const Optional& m1, const Optional& m2) { | |
| 181 return m1.has_value_ && m2.has_value_ ? m1.value_ == m2.value_ | |
| 182 : m1.has_value_ == m2.has_value_; | |
| 183 } | |
| 184 friend bool operator!=(const Optional& m1, const Optional& m2) { | |
| 185 return m1.has_value_ && m2.has_value_ ? m1.value_ != m2.value_ | |
| 186 : m1.has_value_ != m2.has_value_; | |
| 187 } | |
| 188 | |
| 189 private: | |
| 190 bool has_value_; // True iff value_ contains a live value. | |
| 191 union { | 46 union { |
| 192 // By placing value_ in a union, we get to manage its construction and | 47 char empty_; |
| 193 // destruction manually: the Optional constructors won't automatically | |
| 194 // construct it, and the Optional destructor won't automatically destroy | |
| 195 // it. Basically, this just allocates a properly sized and aligned block of | |
| 196 // memory in which we can manually put a T with placement new. | |
| 197 T value_; | 48 T value_; |
| 198 }; | 49 }; |
| 199 }; | 50 }; |
| 200 | 51 |
| 52 template <typename T> |
| 53 struct OptionalStorage<T, true> { |
| 54 OptionalStorage() : empty_('\0') {}; |
| 55 // When T is trivially destructible (i.e. its destructor does nothing) |
| 56 // there is no need to call it. |
| 57 // Since |rtc::AlignedMemory| is just an array its destructor |
| 58 // is trivial. Explicitly defaulting the destructor means it's not |
| 59 // user-provided. All of this together make this destructor trivial. |
| 60 ~OptionalStorage() = default; |
| 61 |
| 62 bool is_null_ = true; |
| 63 union { |
| 64 char empty_; |
| 65 T value_; |
| 66 }; |
| 67 }; |
| 68 |
| 69 } // namespace internal |
| 70 |
| 71 // rtc::Optional has been borrowed from Chromium's base::Optional, with some |
| 72 // alterations. It is a version of the C++17 optional class: |
| 73 // std::optional documentation: |
| 74 // http://en.cppreference.com/w/cpp/utility/optional Chromium documentation: |
| 75 // https://chromium.googlesource.com/chromium/src/+/master/docs/optional.md |
| 76 // |
| 77 // These are the differences between the specification and the implementation: |
| 78 // - The constructor and emplace method using initializer_list are not |
| 79 // implemented because 'initializer_list' is banned from WebRTC. |
| 80 // - Constructors do not use 'constexpr' as it is a C++14 extension. |
| 81 // - 'constexpr' might be missing in some places for reasons specified locally. |
| 82 // - No exceptions are thrown, because they are banned from WebRTC. |
| 83 // - All the non-members are in the 'rtc' namespace instead of 'std'. |
| 84 // |
| 85 // The WebRTC version uses this one neat union trick to implement |
| 86 // OptionalStorage rather than using Chromium's AlignedStorage. |
| 87 template <typename T> |
| 88 class Optional { |
| 89 public: |
| 90 using value_type = T; |
| 91 |
| 92 constexpr Optional() {} |
| 93 Optional(rtc::nullopt_t) {} |
| 94 |
| 95 Optional(const Optional& other) { |
| 96 if (!other.storage_.is_null_) |
| 97 Init(other.value()); |
| 98 } |
| 99 |
| 100 Optional(Optional&& other) { |
| 101 if (!other.storage_.is_null_) |
| 102 Init(std::move(other.value())); |
| 103 } |
| 104 |
| 105 Optional(const T& value) { Init(value); } |
| 106 |
| 107 Optional(T&& value) { Init(std::move(value)); } |
| 108 |
| 109 template <class... Args> |
| 110 explicit Optional(rtc::in_place_t, Args&&... args) { |
| 111 emplace(std::forward<Args>(args)...); |
| 112 } |
| 113 |
| 114 ~Optional() = default; |
| 115 |
| 116 Optional& operator=(rtc::nullopt_t) { |
| 117 FreeIfNeeded(); |
| 118 return *this; |
| 119 } |
| 120 |
| 121 Optional& operator=(const Optional& other) { |
| 122 if (other.storage_.is_null_) { |
| 123 FreeIfNeeded(); |
| 124 return *this; |
| 125 } |
| 126 |
| 127 InitOrAssign(other.value()); |
| 128 return *this; |
| 129 } |
| 130 |
| 131 Optional& operator=(Optional&& other) { |
| 132 if (other.storage_.is_null_) { |
| 133 FreeIfNeeded(); |
| 134 return *this; |
| 135 } |
| 136 |
| 137 InitOrAssign(std::move(other.value())); |
| 138 return *this; |
| 139 } |
| 140 |
| 141 template <class U> |
| 142 typename std::enable_if<std::is_same<std::decay<U>, T>::value, |
| 143 Optional&>::type |
| 144 operator=(U&& value) { |
| 145 InitOrAssign(std::forward<U>(value)); |
| 146 return *this; |
| 147 } |
| 148 |
| 149 // TODO(mlamouri): can't use 'constexpr' with RTC_DCHECK. |
| 150 const T* operator->() const { |
| 151 RTC_DCHECK(!storage_.is_null_); |
| 152 return &value(); |
| 153 } |
| 154 |
| 155 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was |
| 156 // meant to be 'constexpr const'. |
| 157 T* operator->() { |
| 158 RTC_DCHECK(!storage_.is_null_); |
| 159 return &value(); |
| 160 } |
| 161 |
| 162 constexpr const T& operator*() const& { return value(); } |
| 163 |
| 164 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was |
| 165 // meant to be 'constexpr const'. |
| 166 T& operator*() & { return value(); } |
| 167 |
| 168 constexpr const T&& operator*() const&& { return std::move(value()); } |
| 169 |
| 170 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was |
| 171 // meant to be 'constexpr const'. |
| 172 T&& operator*() && { return std::move(value()); } |
| 173 |
| 174 constexpr explicit operator bool() const { return !storage_.is_null_; } |
| 175 |
| 176 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was |
| 177 // meant to be 'constexpr const'. |
| 178 T& value() & { |
| 179 RTC_DCHECK(!storage_.is_null_); |
| 180 return storage_.value_; |
| 181 } |
| 182 |
| 183 // TODO(mlamouri): can't use 'constexpr' with RTC_DCHECK. |
| 184 const T& value() const& { |
| 185 RTC_DCHECK(!storage_.is_null_); |
| 186 return storage_.value_; |
| 187 } |
| 188 |
| 189 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was |
| 190 // meant to be 'constexpr const'. |
| 191 T&& value() && { |
| 192 RTC_DCHECK(!storage_.is_null_); |
| 193 return std::move(storage_.value_); |
| 194 } |
| 195 |
| 196 // TODO(mlamouri): can't use 'constexpr' with RTC_DCHECK. |
| 197 const T&& value() const&& { |
| 198 RTC_DCHECK(!storage_.is_null_); |
| 199 return std::move(storage_.value_); |
| 200 } |
| 201 |
| 202 template <class U> |
| 203 constexpr T value_or(U&& default_value) const& { |
| 204 // TODO(mlamouri): add the following assert when possible: |
| 205 // static_assert(std::is_copy_constructible<T>::value, |
| 206 // "T must be copy constructible"); |
| 207 static_assert(std::is_convertible<U, T>::value, |
| 208 "U must be convertible to T"); |
| 209 return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value)) |
| 210 : value(); |
| 211 } |
| 212 |
| 213 template <class U> |
| 214 T value_or(U&& default_value) && { |
| 215 // TODO(mlamouri): add the following assert when possible: |
| 216 // static_assert(std::is_move_constructible<T>::value, |
| 217 // "T must be move constructible"); |
| 218 static_assert(std::is_convertible<U, T>::value, |
| 219 "U must be convertible to T"); |
| 220 return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value)) |
| 221 : std::move(value()); |
| 222 } |
| 223 |
| 224 void swap(Optional& other) { |
| 225 if (storage_.is_null_ && other.storage_.is_null_) |
| 226 return; |
| 227 |
| 228 if (storage_.is_null_ != other.storage_.is_null_) { |
| 229 if (storage_.is_null_) { |
| 230 Init(std::move(other.storage_.value_)); |
| 231 other.FreeIfNeeded(); |
| 232 } else { |
| 233 other.Init(std::move(storage_.value_)); |
| 234 FreeIfNeeded(); |
| 235 } |
| 236 return; |
| 237 } |
| 238 |
| 239 RTC_DCHECK(!storage_.is_null_ && !other.storage_.is_null_); |
| 240 using std::swap; |
| 241 swap(**this, *other); |
| 242 } |
| 243 |
| 244 template <class... Args> |
| 245 void emplace(Args&&... args) { |
| 246 FreeIfNeeded(); |
| 247 Init(std::forward<Args>(args)...); |
| 248 } |
| 249 |
| 250 private: |
| 251 void Init(const T& value) { |
| 252 RTC_DCHECK(storage_.is_null_); |
| 253 new (&storage_.value_) T(value); |
| 254 storage_.is_null_ = false; |
| 255 } |
| 256 |
| 257 void Init(T&& value) { |
| 258 RTC_DCHECK(storage_.is_null_); |
| 259 new (&storage_.value_) T(std::move(value)); |
| 260 storage_.is_null_ = false; |
| 261 } |
| 262 |
| 263 template <class... Args> |
| 264 void Init(Args&&... args) { |
| 265 RTC_DCHECK(storage_.is_null_); |
| 266 new (&storage_.value_) T(std::forward<Args>(args)...); |
| 267 storage_.is_null_ = false; |
| 268 } |
| 269 |
| 270 void InitOrAssign(const T& value) { |
| 271 if (storage_.is_null_) |
| 272 Init(value); |
| 273 else |
| 274 storage_.value_ = value; |
| 275 } |
| 276 |
| 277 void InitOrAssign(T&& value) { |
| 278 if (storage_.is_null_) |
| 279 Init(std::move(value)); |
| 280 else |
| 281 storage_.value_ = std::move(value); |
| 282 } |
| 283 |
| 284 void FreeIfNeeded() { |
| 285 if (storage_.is_null_) |
| 286 return; |
| 287 storage_.value_.~T(); |
| 288 storage_.is_null_ = true; |
| 289 } |
| 290 |
| 291 internal::OptionalStorage<T> storage_; |
| 292 }; |
| 293 |
| 294 template <class T> |
| 295 constexpr bool operator==(const Optional<T>& lhs, const Optional<T>& rhs) { |
| 296 return !!lhs != !!rhs ? false : lhs == nullopt || (*lhs == *rhs); |
| 297 } |
| 298 |
| 299 template <class T> |
| 300 constexpr bool operator!=(const Optional<T>& lhs, const Optional<T>& rhs) { |
| 301 return !(lhs == rhs); |
| 302 } |
| 303 |
| 304 template <class T> |
| 305 constexpr bool operator<(const Optional<T>& lhs, const Optional<T>& rhs) { |
| 306 return rhs == nullopt ? false : (lhs == nullopt ? true : *lhs < *rhs); |
| 307 } |
| 308 |
| 309 template <class T> |
| 310 constexpr bool operator<=(const Optional<T>& lhs, const Optional<T>& rhs) { |
| 311 return !(rhs < lhs); |
| 312 } |
| 313 |
| 314 template <class T> |
| 315 constexpr bool operator>(const Optional<T>& lhs, const Optional<T>& rhs) { |
| 316 return rhs < lhs; |
| 317 } |
| 318 |
| 319 template <class T> |
| 320 constexpr bool operator>=(const Optional<T>& lhs, const Optional<T>& rhs) { |
| 321 return !(lhs < rhs); |
| 322 } |
| 323 |
| 324 template <class T> |
| 325 constexpr bool operator==(const Optional<T>& opt, rtc::nullopt_t) { |
| 326 return !opt; |
| 327 } |
| 328 |
| 329 template <class T> |
| 330 constexpr bool operator==(rtc::nullopt_t, const Optional<T>& opt) { |
| 331 return !opt; |
| 332 } |
| 333 |
| 334 template <class T> |
| 335 constexpr bool operator!=(const Optional<T>& opt, rtc::nullopt_t) { |
| 336 return !!opt; |
| 337 } |
| 338 |
| 339 template <class T> |
| 340 constexpr bool operator!=(rtc::nullopt_t, const Optional<T>& opt) { |
| 341 return !!opt; |
| 342 } |
| 343 |
| 344 template <class T> |
| 345 constexpr bool operator<(const Optional<T>& opt, rtc::nullopt_t) { |
| 346 return false; |
| 347 } |
| 348 |
| 349 template <class T> |
| 350 constexpr bool operator<(rtc::nullopt_t, const Optional<T>& opt) { |
| 351 return !!opt; |
| 352 } |
| 353 |
| 354 template <class T> |
| 355 constexpr bool operator<=(const Optional<T>& opt, rtc::nullopt_t) { |
| 356 return !opt; |
| 357 } |
| 358 |
| 359 template <class T> |
| 360 constexpr bool operator<=(rtc::nullopt_t, const Optional<T>& opt) { |
| 361 return true; |
| 362 } |
| 363 |
| 364 template <class T> |
| 365 constexpr bool operator>(const Optional<T>& opt, rtc::nullopt_t) { |
| 366 return !!opt; |
| 367 } |
| 368 |
| 369 template <class T> |
| 370 constexpr bool operator>(rtc::nullopt_t, const Optional<T>& opt) { |
| 371 return false; |
| 372 } |
| 373 |
| 374 template <class T> |
| 375 constexpr bool operator>=(const Optional<T>& opt, rtc::nullopt_t) { |
| 376 return true; |
| 377 } |
| 378 |
| 379 template <class T> |
| 380 constexpr bool operator>=(rtc::nullopt_t, const Optional<T>& opt) { |
| 381 return !opt; |
| 382 } |
| 383 |
| 384 template <class T> |
| 385 constexpr bool operator==(const Optional<T>& opt, const T& value) { |
| 386 return opt != nullopt ? *opt == value : false; |
| 387 } |
| 388 |
| 389 template <class T> |
| 390 constexpr bool operator==(const T& value, const Optional<T>& opt) { |
| 391 return opt == value; |
| 392 } |
| 393 |
| 394 template <class T> |
| 395 constexpr bool operator!=(const Optional<T>& opt, const T& value) { |
| 396 return !(opt == value); |
| 397 } |
| 398 |
| 399 template <class T> |
| 400 constexpr bool operator!=(const T& value, const Optional<T>& opt) { |
| 401 return !(opt == value); |
| 402 } |
| 403 |
| 404 template <class T> |
| 405 constexpr bool operator<(const Optional<T>& opt, const T& value) { |
| 406 return opt != nullopt ? *opt < value : true; |
| 407 } |
| 408 |
| 409 template <class T> |
| 410 constexpr bool operator<(const T& value, const Optional<T>& opt) { |
| 411 return opt != nullopt ? value < *opt : false; |
| 412 } |
| 413 |
| 414 template <class T> |
| 415 constexpr bool operator<=(const Optional<T>& opt, const T& value) { |
| 416 return !(opt > value); |
| 417 } |
| 418 |
| 419 template <class T> |
| 420 constexpr bool operator<=(const T& value, const Optional<T>& opt) { |
| 421 return !(value > opt); |
| 422 } |
| 423 |
| 424 template <class T> |
| 425 constexpr bool operator>(const Optional<T>& opt, const T& value) { |
| 426 return value < opt; |
| 427 } |
| 428 |
| 429 template <class T> |
| 430 constexpr bool operator>(const T& value, const Optional<T>& opt) { |
| 431 return opt < value; |
| 432 } |
| 433 |
| 434 template <class T> |
| 435 constexpr bool operator>=(const Optional<T>& opt, const T& value) { |
| 436 return !(opt < value); |
| 437 } |
| 438 |
| 439 template <class T> |
| 440 constexpr bool operator>=(const T& value, const Optional<T>& opt) { |
| 441 return !(value < opt); |
| 442 } |
| 443 |
| 444 template <class T> |
| 445 constexpr Optional<typename std::decay<T>::type> make_optional(T&& value) { |
| 446 return Optional<typename std::decay<T>::type>(std::forward<T>(value)); |
| 447 } |
| 448 |
| 449 template <class T> |
| 450 void swap(Optional<T>& lhs, Optional<T>& rhs) { |
| 451 lhs.swap(rhs); |
| 452 } |
| 453 |
| 201 } // namespace rtc | 454 } // namespace rtc |
| 202 | 455 |
| 456 namespace std { |
| 457 |
| 458 template <class T> |
| 459 struct hash<rtc::Optional<T>> { |
| 460 size_t operator()(const rtc::Optional<T>& opt) const { |
| 461 return opt == rtc::nullopt ? 0 : std::hash<T>()(*opt); |
| 462 } |
| 463 }; |
| 464 |
| 465 } // namespace std |
| 466 |
| 203 #endif // WEBRTC_BASE_OPTIONAL_H_ | 467 #endif // WEBRTC_BASE_OPTIONAL_H_ |
| OLD | NEW |