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 |