1 /* Copyright 2017 The TensorFlow Authors. All Rights Reserved. 2 3 Licensed under the Apache License, Version 2.0 (the "License"); 4 you may not use this file except in compliance with the License. 5 You may obtain a copy of the License at 6 7 http://www.apache.org/licenses/LICENSE-2.0 8 9 Unless required by applicable law or agreed to in writing, software 10 distributed under the License is distributed on an "AS IS" BASIS, 11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 See the License for the specific language governing permissions and 13 limitations under the License. 14 ==============================================================================*/ 15 16 #ifndef TENSORFLOW_LIB_GTL_OPTIONAL_H_ 17 #define TENSORFLOW_LIB_GTL_OPTIONAL_H_ 18 19 #include <assert.h> 20 #include <functional> 21 #include <initializer_list> 22 #include <type_traits> 23 #include <utility> 24 25 #include "tensorflow/core/platform/logging.h" 26 27 namespace tensorflow { 28 namespace gtl { 29 30 // A value of type gtl::optional<T> holds either a value of T or an 31 // "empty" value. When it holds a value of T, it stores it as a direct 32 // subobject, so sizeof(optional<T>) is approximately sizeof(T)+1. The interface 33 // is based on the upcoming std::optional<T>, and gtl::optional<T> is 34 // designed to be cheaply drop-in replaceable by std::optional<T>, once it is 35 // rolled out. 36 // 37 // This implementation is based on the specification in the latest draft as of 38 // 2017-01-05, section 20.6. 39 // 40 // Differences between gtl::optional<T> and std::optional<T> include: 41 // - constexpr not used for nonconst member functions. 42 // (dependency on some differences between C++11 and C++14.) 43 // - nullopt and in_place are not constexpr. We need the inline variable 44 // support in C++17 for external linkage. 45 // - CHECK instead of throwing std::bad_optional_access. 46 // - optional::swap() and swap() relies on std::is_(nothrow_)swappable 47 // which is introduced in C++17. So we assume is_swappable is always true 48 // and is_nothrow_swappable is same as std::is_trivial. 49 // - make_optional cannot be constexpr due to absence of guaranteed copy 50 // elision. 51 // 52 // Synopsis: 53 // 54 // #include "tensorflow/core/lib/gtl/optional.h" 55 // 56 // tensorflow::gtl::optional<string> f() { 57 // string result; 58 // if (...) { 59 // ... 60 // result = ...; 61 // return result; 62 // } else { 63 // ... 64 // return tensorflow::gtl::nullopt; 65 // } 66 // } 67 // 68 // int main() { 69 // tensorflow::gtl::optional<string> optstr = f(); 70 // if (optstr) { 71 // // non-empty 72 // print(optstr.value()); 73 // } else { 74 // // empty 75 // error(); 76 // } 77 // } 78 template <typename T> 79 class optional; 80 81 // The tag constant `in_place` is used as the first parameter of an optional<T> 82 // constructor to indicate that the remaining arguments should be forwarded 83 // to the underlying T constructor. 84 struct in_place_t {}; 85 extern const in_place_t in_place; 86 87 // The tag constant `nullopt` is used to indicate an empty optional<T> in 88 // certain functions, such as construction or assignment. 89 struct nullopt_t { 90 struct init_t {}; 91 static init_t init; 92 // It must not be default-constructible to avoid ambiguity for opt = {}. 93 // Note the non-const reference, it is to eliminate ambiguity for code like: 94 // struct S { int value; }; 95 // 96 // void Test() { 97 // optional<S> opt; 98 // opt = {{}}; 99 // } 100 explicit constexpr nullopt_t(init_t& /*unused*/) {} // NOLINT 101 }; 102 extern const nullopt_t nullopt; 103 104 namespace internal_optional { 105 106 // define forward locally because std::forward is not constexpr until C++14 107 template <typename T> 108 constexpr T&& forward(typename std::remove_reference<T>::type& 109 t) noexcept { // NOLINT(runtime/references) 110 return static_cast<T&&>(t); 111 } 112 113 struct empty_struct {}; 114 // This class stores the data in optional<T>. 115 // It is specialized based on whether T is trivially destructible. 116 // This is the specialization for non trivially destructible type. 117 template <typename T, bool = std::is_trivially_destructible<T>::value> 118 class optional_data_dtor_base { 119 protected: 120 // Whether there is data or not. 121 bool engaged_; 122 // data storage 123 union { 124 empty_struct dummy_; 125 T data_; 126 }; 127 128 void destruct() noexcept { 129 if (engaged_) { 130 data_.~T(); 131 engaged_ = false; 132 } 133 } 134 135 // dummy_ must be initialized for constexpr constructor 136 constexpr optional_data_dtor_base() noexcept : engaged_(false), dummy_{} {} 137 138 template <typename... Args> 139 constexpr explicit optional_data_dtor_base(in_place_t, Args&&... args) 140 : engaged_(true), data_(internal_optional::forward<Args>(args)...) {} 141 142 ~optional_data_dtor_base() { destruct(); } 143 }; 144 145 // Specialization for trivially destructible type. 146 template <typename T> 147 class optional_data_dtor_base<T, true> { 148 protected: 149 // Whether there is data or not. 150 bool engaged_; 151 // data storage 152 union { 153 empty_struct dummy_; 154 T data_; 155 }; 156 void destruct() noexcept { engaged_ = false; } 157 158 // dummy_ must be initialized for constexpr constructor 159 constexpr optional_data_dtor_base() noexcept : engaged_(false), dummy_{} {} 160 161 template <typename... Args> 162 constexpr explicit optional_data_dtor_base(in_place_t, Args&&... args) 163 : engaged_(true), data_(internal_optional::forward<Args>(args)...) {} 164 165 ~optional_data_dtor_base() = default; 166 }; 167 168 template <typename T> 169 class optional_data : public optional_data_dtor_base<T> { 170 protected: 171 using base = optional_data_dtor_base<T>; 172 using base::base; 173 174 T* pointer() { return &this->data_; } 175 176 constexpr const T* pointer() const { return &this->data_; } 177 178 template <typename... Args> 179 void construct(Args&&... args) { 180 new (pointer()) T(std::forward<Args>(args)...); 181 this->engaged_ = true; 182 } 183 184 template <typename U> 185 void assign(U&& u) { 186 if (this->engaged_) { 187 this->data_ = std::forward<U>(u); 188 } else { 189 construct(std::forward<U>(u)); 190 } 191 } 192 193 optional_data() = default; 194 195 optional_data(const optional_data& rhs) { 196 if (rhs.engaged_) { 197 construct(rhs.data_); 198 } 199 } 200 201 optional_data(optional_data&& rhs) noexcept( 202 std::is_nothrow_move_constructible<T>::value) { 203 if (rhs.engaged_) { 204 construct(std::move(rhs.data_)); 205 } 206 } 207 208 optional_data& operator=(const optional_data& rhs) { 209 if (rhs.engaged_) { 210 assign(rhs.data_); 211 } else { 212 this->destruct(); 213 } 214 return *this; 215 } 216 217 optional_data& operator=(optional_data&& rhs) noexcept( 218 std::is_nothrow_move_assignable<T>::value&& 219 std::is_nothrow_move_constructible<T>::value) { 220 if (rhs.engaged_) { 221 assign(std::move(rhs.data_)); 222 } else { 223 this->destruct(); 224 } 225 return *this; 226 } 227 }; 228 229 // ordered by level of restriction, from low to high. 230 // copyable implies movable. 231 enum class copy_traits { copyable = 0, movable = 1, non_movable = 2 }; 232 233 // base class for enabling/disabling copy/move constructor. 234 template <copy_traits> 235 class optional_ctor_base; 236 237 template <> 238 class optional_ctor_base<copy_traits::copyable> { 239 public: 240 constexpr optional_ctor_base() = default; 241 optional_ctor_base(const optional_ctor_base&) = default; 242 optional_ctor_base(optional_ctor_base&&) = default; 243 optional_ctor_base& operator=(const optional_ctor_base&) = default; 244 optional_ctor_base& operator=(optional_ctor_base&&) = default; 245 }; 246 247 template <> 248 class optional_ctor_base<copy_traits::movable> { 249 public: 250 constexpr optional_ctor_base() = default; 251 optional_ctor_base(const optional_ctor_base&) = delete; 252 optional_ctor_base(optional_ctor_base&&) = default; 253 optional_ctor_base& operator=(const optional_ctor_base&) = default; 254 optional_ctor_base& operator=(optional_ctor_base&&) = default; 255 }; 256 257 template <> 258 class optional_ctor_base<copy_traits::non_movable> { 259 public: 260 constexpr optional_ctor_base() = default; 261 optional_ctor_base(const optional_ctor_base&) = delete; 262 optional_ctor_base(optional_ctor_base&&) = delete; 263 optional_ctor_base& operator=(const optional_ctor_base&) = default; 264 optional_ctor_base& operator=(optional_ctor_base&&) = default; 265 }; 266 267 // base class for enabling/disabling copy/move assignment. 268 template <copy_traits> 269 class optional_assign_base; 270 271 template <> 272 class optional_assign_base<copy_traits::copyable> { 273 public: 274 constexpr optional_assign_base() = default; 275 optional_assign_base(const optional_assign_base&) = default; 276 optional_assign_base(optional_assign_base&&) = default; 277 optional_assign_base& operator=(const optional_assign_base&) = default; 278 optional_assign_base& operator=(optional_assign_base&&) = default; 279 }; 280 281 template <> 282 class optional_assign_base<copy_traits::movable> { 283 public: 284 constexpr optional_assign_base() = default; 285 optional_assign_base(const optional_assign_base&) = default; 286 optional_assign_base(optional_assign_base&&) = default; 287 optional_assign_base& operator=(const optional_assign_base&) = delete; 288 optional_assign_base& operator=(optional_assign_base&&) = default; 289 }; 290 291 template <> 292 class optional_assign_base<copy_traits::non_movable> { 293 public: 294 constexpr optional_assign_base() = default; 295 optional_assign_base(const optional_assign_base&) = default; 296 optional_assign_base(optional_assign_base&&) = default; 297 optional_assign_base& operator=(const optional_assign_base&) = delete; 298 optional_assign_base& operator=(optional_assign_base&&) = delete; 299 }; 300 301 template <typename T> 302 constexpr copy_traits get_ctor_copy_traits() { 303 return std::is_copy_constructible<T>::value 304 ? copy_traits::copyable 305 : std::is_move_constructible<T>::value ? copy_traits::movable 306 : copy_traits::non_movable; 307 } 308 309 template <typename T> 310 constexpr copy_traits get_assign_copy_traits() { 311 return std::is_copy_assignable<T>::value && 312 std::is_copy_constructible<T>::value 313 ? copy_traits::copyable 314 : std::is_move_assignable<T>::value && 315 std::is_move_constructible<T>::value 316 ? copy_traits::movable 317 : copy_traits::non_movable; 318 } 319 320 // Whether T is constructible or convertible from optional<U>. 321 template <typename T, typename U> 322 struct is_constructible_convertible_from_optional 323 : std::integral_constant< 324 bool, std::is_constructible<T, optional<U>&>::value || 325 std::is_constructible<T, optional<U>&&>::value || 326 std::is_constructible<T, const optional<U>&>::value || 327 std::is_constructible<T, const optional<U>&&>::value || 328 std::is_convertible<optional<U>&, T>::value || 329 std::is_convertible<optional<U>&&, T>::value || 330 std::is_convertible<const optional<U>&, T>::value || 331 std::is_convertible<const optional<U>&&, T>::value> {}; 332 333 // Whether T is constructible or convertible or assignable from optional<U>. 334 template <typename T, typename U> 335 struct is_constructible_convertible_assignable_from_optional 336 : std::integral_constant< 337 bool, is_constructible_convertible_from_optional<T, U>::value || 338 std::is_assignable<T&, optional<U>&>::value || 339 std::is_assignable<T&, optional<U>&&>::value || 340 std::is_assignable<T&, const optional<U>&>::value || 341 std::is_assignable<T&, const optional<U>&&>::value> {}; 342 343 } // namespace internal_optional 344 345 template <typename T> 346 class optional : private internal_optional::optional_data<T>, 347 private internal_optional::optional_ctor_base< 348 internal_optional::get_ctor_copy_traits<T>()>, 349 private internal_optional::optional_assign_base< 350 internal_optional::get_assign_copy_traits<T>()> { 351 using data_base = internal_optional::optional_data<T>; 352 353 public: 354 typedef T value_type; 355 356 // [optional.ctor], constructors 357 358 // A default constructed optional holds the empty value, NOT a default 359 // constructed T. 360 constexpr optional() noexcept {} 361 362 // An optional initialized with `nullopt` holds the empty value. 363 constexpr optional(nullopt_t) noexcept {} // NOLINT(runtime/explicit) 364 365 // Copy constructor, standard semantics. 366 optional(const optional& src) = default; 367 368 // Move constructor, standard semantics. 369 optional(optional&& src) = default; 370 371 // optional<T>(in_place, arg1, arg2, arg3) constructs a non-empty optional 372 // with an in-place constructed value of T(arg1,arg2,arg3). 373 // TODO(b/34201852): Add std::is_constructible<T, Args&&...> SFINAE. 374 template <typename... Args> 375 constexpr explicit optional(in_place_t, Args&&... args) 376 : data_base(in_place_t(), internal_optional::forward<Args>(args)...) {} 377 378 // optional<T>(in_place, {arg1, arg2, arg3}) constructs a non-empty optional 379 // with an in-place list-initialized value of T({arg1, arg2, arg3}). 380 template <typename U, typename... Args, 381 typename = typename std::enable_if<std::is_constructible< 382 T, std::initializer_list<U>&, Args&&...>::value>::type> 383 constexpr explicit optional(in_place_t, std::initializer_list<U> il, 384 Args&&... args) 385 : data_base(in_place_t(), il, internal_optional::forward<Args>(args)...) { 386 } 387 388 template < 389 typename U = T, 390 typename std::enable_if< 391 std::is_constructible<T, U&&>::value && 392 !std::is_same<in_place_t, typename std::decay<U>::type>::value && 393 !std::is_same<optional<T>, typename std::decay<U>::type>::value && 394 std::is_convertible<U&&, T>::value, 395 bool>::type = false> 396 constexpr optional(U&& v) // NOLINT 397 : data_base(in_place_t(), internal_optional::forward<U>(v)) {} 398 399 template < 400 typename U = T, 401 typename std::enable_if< 402 std::is_constructible<T, U&&>::value && 403 !std::is_same<in_place_t, typename std::decay<U>::type>::value && 404 !std::is_same<optional<T>, typename std::decay<U>::type>::value && 405 !std::is_convertible<U&&, T>::value, 406 bool>::type = false> 407 explicit constexpr optional(U&& v) 408 : data_base(in_place_t(), internal_optional::forward<U>(v)) {} 409 410 // Converting copy constructor (implicit) 411 template < 412 typename U, 413 typename std::enable_if< 414 std::is_constructible<T, const U&>::value && 415 !internal_optional::is_constructible_convertible_from_optional< 416 T, U>::value && 417 std::is_convertible<const U&, T>::value, 418 bool>::type = false> 419 optional(const optional<U>& rhs) { // NOLINT 420 if (rhs) { 421 this->construct(*rhs); 422 } 423 } 424 425 // Converting copy constructor (explicit) 426 template < 427 typename U, 428 typename std::enable_if< 429 std::is_constructible<T, const U&>::value && 430 !internal_optional::is_constructible_convertible_from_optional< 431 T, U>::value && 432 !std::is_convertible<const U&, T>::value, 433 bool>::type = false> 434 explicit optional(const optional<U>& rhs) { 435 if (rhs) { 436 this->construct(*rhs); 437 } 438 } 439 440 // Converting move constructor (implicit) 441 template < 442 typename U, 443 typename std::enable_if< 444 std::is_constructible<T, U&&>::value && 445 !internal_optional::is_constructible_convertible_from_optional< 446 T, U>::value && 447 std::is_convertible<U&&, T>::value, 448 bool>::type = false> 449 optional(optional<U>&& rhs) { // NOLINT 450 if (rhs) { 451 this->construct(std::move(*rhs)); 452 } 453 } 454 455 // Converting move constructor (explicit) 456 template < 457 typename U, 458 typename std::enable_if< 459 std::is_constructible<T, U&&>::value && 460 !internal_optional::is_constructible_convertible_from_optional< 461 T, U>::value && 462 !std::is_convertible<U&&, T>::value, 463 bool>::type = false> 464 explicit optional(optional<U>&& rhs) { 465 if (rhs) { 466 this->construct(std::move(*rhs)); 467 } 468 } 469 470 // [optional.dtor], destructor, trivial if T is trivially destructible. 471 ~optional() = default; 472 473 // [optional.assign], assignment 474 475 // Assignment from nullopt: opt = nullopt 476 optional& operator=(nullopt_t) noexcept { 477 this->destruct(); 478 return *this; 479 } 480 481 // Copy assignment, standard semantics. 482 optional& operator=(const optional& src) = default; 483 484 // Move assignment, standard semantics. 485 optional& operator=(optional&& src) = default; 486 487 // Value assignment 488 template < 489 typename U = T, 490 typename = typename std::enable_if< 491 !std::is_same<optional<T>, typename std::decay<U>::type>::value && 492 (!std::is_scalar<T>::value || 493 !std::is_same<T, typename std::decay<U>::type>::value) && 494 std::is_constructible<T, U>::value && 495 std::is_assignable<T&, U>::value>::type> 496 optional& operator=(U&& v) { 497 this->assign(std::forward<U>(v)); 498 return *this; 499 } 500 501 template <typename U, 502 typename = typename std::enable_if< 503 std::is_constructible<T, const U&>::value && 504 std::is_assignable<T&, const U&>::value && 505 !internal_optional:: 506 is_constructible_convertible_assignable_from_optional< 507 T, U>::value>::type> 508 optional& operator=(const optional<U>& rhs) { 509 if (rhs) { 510 this->assign(*rhs); 511 } else { 512 this->destruct(); 513 } 514 return *this; 515 } 516 517 template <typename U, 518 typename = typename std::enable_if< 519 std::is_constructible<T, U>::value && 520 std::is_assignable<T&, U>::value && 521 !internal_optional:: 522 is_constructible_convertible_assignable_from_optional< 523 T, U>::value>::type> 524 optional& operator=(optional<U>&& rhs) { 525 if (rhs) { 526 this->assign(std::move(*rhs)); 527 } else { 528 this->destruct(); 529 } 530 return *this; 531 } 532 533 // [optional.mod], modifiers 534 // Destroys the inner T value if one is present. 535 void reset() noexcept { this->destruct(); } 536 537 // Emplace reconstruction. (Re)constructs the underlying T in-place with the 538 // given arguments forwarded: 539 // 540 // optional<Foo> opt; 541 // opt.emplace(arg1,arg2,arg3); (Constructs Foo(arg1,arg2,arg3)) 542 // 543 // If the optional is non-empty, and the `args` refer to subobjects of the 544 // current object, then behavior is undefined. This is because the current 545 // object will be destructed before the new object is constructed with `args`. 546 // 547 template <typename... Args, 548 typename = typename std::enable_if< 549 std::is_constructible<T, Args&&...>::value>::type> 550 void emplace(Args&&... args) { 551 this->destruct(); 552 this->construct(std::forward<Args>(args)...); 553 } 554 555 // Emplace reconstruction with initializer-list. See immediately above. 556 template <class U, class... Args, 557 typename = typename std::enable_if<std::is_constructible< 558 T, std::initializer_list<U>&, Args&&...>::value>::type> 559 void emplace(std::initializer_list<U> il, Args&&... args) { 560 this->destruct(); 561 this->construct(il, std::forward<Args>(args)...); 562 } 563 564 // [optional.swap], swap 565 // Swap, standard semantics. 566 void swap(optional& rhs) noexcept( 567 std::is_nothrow_move_constructible<T>::value&& 568 std::is_trivial<T>::value) { 569 if (*this) { 570 if (rhs) { 571 using std::swap; 572 swap(**this, *rhs); 573 } else { 574 rhs.construct(std::move(**this)); 575 this->destruct(); 576 } 577 } else { 578 if (rhs) { 579 this->construct(std::move(*rhs)); 580 rhs.destruct(); 581 } else { 582 // no effect (swap(disengaged, disengaged)) 583 } 584 } 585 } 586 587 // [optional.observe], observers 588 // You may use `*opt`, and `opt->m`, to access the underlying T value and T's 589 // member `m`, respectively. If the optional is empty, behavior is 590 // undefined. 591 constexpr const T* operator->() const { return this->pointer(); } 592 T* operator->() { 593 assert(this->engaged_); 594 return this->pointer(); 595 } 596 constexpr const T& operator*() const& { return reference(); } 597 T& operator*() & { 598 assert(this->engaged_); 599 return reference(); 600 } 601 constexpr const T&& operator*() const&& { return std::move(reference()); } 602 T&& operator*() && { 603 assert(this->engaged_); 604 return std::move(reference()); 605 } 606 607 // In a bool context an optional<T> will return false if and only if it is 608 // empty. 609 // 610 // if (opt) { 611 // // do something with opt.value(); 612 // } else { 613 // // opt is empty 614 // } 615 // 616 constexpr explicit operator bool() const noexcept { return this->engaged_; } 617 618 // Returns false if and only if *this is empty. 619 constexpr bool has_value() const noexcept { return this->engaged_; } 620 621 // Use `opt.value()` to get a reference to underlying value. The constness 622 // and lvalue/rvalue-ness of `opt` is preserved to the view of the T 623 // subobject. 624 const T& value() const& { 625 CHECK(*this) << "Bad optional access"; 626 return reference(); 627 } 628 T& value() & { 629 CHECK(*this) << "Bad optional access"; 630 return reference(); 631 } 632 T&& value() && { // NOLINT(build/c++11) 633 CHECK(*this) << "Bad optional access"; 634 return std::move(reference()); 635 } 636 const T&& value() const&& { // NOLINT(build/c++11) 637 CHECK(*this) << "Bad optional access"; 638 return std::move(reference()); 639 } 640 641 // Use `opt.value_or(val)` to get either the value of T or the given default 642 // `val` in the empty case. 643 template <class U> 644 constexpr T value_or(U&& v) const& { 645 return static_cast<bool>(*this) ? **this 646 : static_cast<T>(std::forward<U>(v)); 647 } 648 template <class U> 649 T value_or(U&& v) && { // NOLINT(build/c++11) 650 return static_cast<bool>(*this) ? std::move(**this) 651 : static_cast<T>(std::forward<U>(v)); 652 } 653 654 private: 655 // Private accessors for internal storage viewed as reference to T. 656 constexpr const T& reference() const { return *this->pointer(); } 657 T& reference() { return *(this->pointer()); } 658 659 // T constraint checks. You can't have an optional of nullopt_t, in_place_t 660 // or a reference. 661 static_assert( 662 !std::is_same<nullopt_t, typename std::remove_cv<T>::type>::value, 663 "optional<nullopt_t> is not allowed."); 664 static_assert( 665 !std::is_same<in_place_t, typename std::remove_cv<T>::type>::value, 666 "optional<in_place_t> is not allowed."); 667 static_assert(!std::is_reference<T>::value, 668 "optional<reference> is not allowed."); 669 }; 670 671 // [optional.specalg] 672 // Swap, standard semantics. 673 // This function shall not participate in overload resolution unless 674 // is_move_constructible_v<T> is true and is_swappable_v<T> is true. 675 // NOTE: we assume is_swappable is always true. There will be a compiling error 676 // if T is actually not Swappable. 677 template <typename T, 678 typename std::enable_if<std::is_move_constructible<T>::value, 679 bool>::type = false> 680 void swap(optional<T>& a, optional<T>& b) noexcept(noexcept(a.swap(b))) { 681 a.swap(b); 682 } 683 684 // NOTE: make_optional cannot be constexpr in C++11 because the copy/move 685 // constructor is not constexpr and we don't have guaranteed copy elision 686 // util C++17. But they are still declared constexpr for consistency with 687 // the standard. 688 689 // make_optional(v) creates a non-empty optional<T> where the type T is deduced 690 // from v. Can also be explicitly instantiated as make_optional<T>(v). 691 template <typename T> 692 constexpr optional<typename std::decay<T>::type> make_optional(T&& v) { 693 return optional<typename std::decay<T>::type>(std::forward<T>(v)); 694 } 695 696 template <typename T, typename... Args> 697 constexpr optional<T> make_optional(Args&&... args) { 698 return optional<T>(in_place_t(), internal_optional::forward<Args>(args)...); 699 } 700 701 template <typename T, typename U, typename... Args> 702 constexpr optional<T> make_optional(std::initializer_list<U> il, 703 Args&&... args) { 704 return optional<T>(in_place_t(), il, 705 internal_optional::forward<Args>(args)...); 706 } 707 708 // Relational operators. Empty optionals are considered equal to each 709 // other and less than non-empty optionals. Supports relations between 710 // optional<T> and optional<T>, between optional<T> and T, and between 711 // optional<T> and nullopt. 712 // Note: We're careful to support T having non-bool relationals. 713 714 // Relational operators [optional.relops] 715 // The C++17 (N4606) "Returns:" statements are translated into code 716 // in an obvious way here, and the original text retained as function docs. 717 // Returns: If bool(x) != bool(y), false; otherwise if bool(x) == false, true; 718 // otherwise *x == *y. 719 template <class T> 720 constexpr bool operator==(const optional<T>& x, const optional<T>& y) { 721 return static_cast<bool>(x) != static_cast<bool>(y) 722 ? false 723 : static_cast<bool>(x) == false ? true : *x == *y; 724 } 725 // Returns: If bool(x) != bool(y), true; otherwise, if bool(x) == false, false; 726 // otherwise *x != *y. 727 template <class T> 728 constexpr bool operator!=(const optional<T>& x, const optional<T>& y) { 729 return static_cast<bool>(x) != static_cast<bool>(y) 730 ? true 731 : static_cast<bool>(x) == false ? false : *x != *y; 732 } 733 // Returns: If !y, false; otherwise, if !x, true; otherwise *x < *y. 734 template <class T> 735 constexpr bool operator<(const optional<T>& x, const optional<T>& y) { 736 return !y ? false : !x ? true : *x < *y; 737 } 738 // Returns: If !x, false; otherwise, if !y, true; otherwise *x > *y. 739 template <class T> 740 constexpr bool operator>(const optional<T>& x, const optional<T>& y) { 741 return !x ? false : !y ? true : *x > *y; 742 } 743 // Returns: If !x, true; otherwise, if !y, false; otherwise *x <= *y. 744 template <class T> 745 constexpr bool operator<=(const optional<T>& x, const optional<T>& y) { 746 return !x ? true : !y ? false : *x <= *y; 747 } 748 // Returns: If !y, true; otherwise, if !x, false; otherwise *x >= *y. 749 template <class T> 750 constexpr bool operator>=(const optional<T>& x, const optional<T>& y) { 751 return !y ? true : !x ? false : *x >= *y; 752 } 753 754 // Comparison with nullopt [optional.nullops] 755 // The C++17 (N4606) "Returns:" statements are used directly here. 756 template <class T> 757 constexpr bool operator==(const optional<T>& x, nullopt_t) noexcept { 758 return !x; 759 } 760 template <class T> 761 constexpr bool operator==(nullopt_t, const optional<T>& x) noexcept { 762 return !x; 763 } 764 template <class T> 765 constexpr bool operator!=(const optional<T>& x, nullopt_t) noexcept { 766 return static_cast<bool>(x); 767 } 768 template <class T> 769 constexpr bool operator!=(nullopt_t, const optional<T>& x) noexcept { 770 return static_cast<bool>(x); 771 } 772 template <class T> 773 constexpr bool operator<(const optional<T>& x, nullopt_t) noexcept { 774 return false; 775 } 776 template <class T> 777 constexpr bool operator<(nullopt_t, const optional<T>& x) noexcept { 778 return static_cast<bool>(x); 779 } 780 template <class T> 781 constexpr bool operator<=(const optional<T>& x, nullopt_t) noexcept { 782 return !x; 783 } 784 template <class T> 785 constexpr bool operator<=(nullopt_t, const optional<T>& x) noexcept { 786 return true; 787 } 788 template <class T> 789 constexpr bool operator>(const optional<T>& x, nullopt_t) noexcept { 790 return static_cast<bool>(x); 791 } 792 template <class T> 793 constexpr bool operator>(nullopt_t, const optional<T>& x) noexcept { 794 return false; 795 } 796 template <class T> 797 constexpr bool operator>=(const optional<T>& x, nullopt_t) noexcept { 798 return true; 799 } 800 template <class T> 801 constexpr bool operator>=(nullopt_t, const optional<T>& x) noexcept { 802 return !x; 803 } 804 805 // Comparison with T [optional.comp_with_t] 806 // The C++17 (N4606) "Equivalent to:" statements are used directly here. 807 template <class T> 808 constexpr bool operator==(const optional<T>& x, const T& v) { 809 return static_cast<bool>(x) ? *x == v : false; 810 } 811 template <class T> 812 constexpr bool operator==(const T& v, const optional<T>& x) { 813 return static_cast<bool>(x) ? v == *x : false; 814 } 815 template <class T> 816 constexpr bool operator!=(const optional<T>& x, const T& v) { 817 return static_cast<bool>(x) ? *x != v : true; 818 } 819 template <class T> 820 constexpr bool operator!=(const T& v, const optional<T>& x) { 821 return static_cast<bool>(x) ? v != *x : true; 822 } 823 template <class T> 824 constexpr bool operator<(const optional<T>& x, const T& v) { 825 return static_cast<bool>(x) ? *x < v : true; 826 } 827 template <class T> 828 constexpr bool operator<(const T& v, const optional<T>& x) { 829 return static_cast<bool>(x) ? v < *x : false; 830 } 831 template <class T> 832 constexpr bool operator<=(const optional<T>& x, const T& v) { 833 return static_cast<bool>(x) ? *x <= v : true; 834 } 835 template <class T> 836 constexpr bool operator<=(const T& v, const optional<T>& x) { 837 return static_cast<bool>(x) ? v <= *x : false; 838 } 839 template <class T> 840 constexpr bool operator>(const optional<T>& x, const T& v) { 841 return static_cast<bool>(x) ? *x > v : false; 842 } 843 template <class T> 844 constexpr bool operator>(const T& v, const optional<T>& x) { 845 return static_cast<bool>(x) ? v > *x : true; 846 } 847 template <class T> 848 constexpr bool operator>=(const optional<T>& x, const T& v) { 849 return static_cast<bool>(x) ? *x >= v : false; 850 } 851 template <class T> 852 constexpr bool operator>=(const T& v, const optional<T>& x) { 853 return static_cast<bool>(x) ? v >= *x : true; 854 } 855 856 } // namespace gtl 857 } // namespace tensorflow 858 859 namespace std { 860 861 // Normally std::hash specializations are not recommended in tensorflow code, 862 // but we allow this as it is following a standard library component. 863 template <class T> 864 struct hash<::tensorflow::gtl::optional<T>> { 865 size_t operator()(const ::tensorflow::gtl::optional<T>& opt) const { 866 if (opt) { 867 return hash<T>()(*opt); 868 } else { 869 return static_cast<size_t>(0x297814aaad196e6dULL); 870 } 871 } 872 }; 873 874 } // namespace std 875 876 #endif // TENSORFLOW_LIB_GTL_OPTIONAL_H_ 877