| Index: webrtc/base/optional.h
|
| diff --git a/webrtc/base/optional.h b/webrtc/base/optional.h
|
| index 7320533c6322864a72ac0ef1bebe38ce30e316d2..25cfbfe41752597d340864f3cc46ce56794763b4 100644
|
| --- a/webrtc/base/optional.h
|
| +++ b/webrtc/base/optional.h
|
| @@ -19,11 +19,7 @@
|
|
|
| namespace rtc {
|
|
|
| -// Simple std::experimental::optional-wannabe. It either contains a T or not.
|
| -// In order to keep the implementation simple and portable, this implementation
|
| -// actually contains a (default-constructed) T even when it supposedly doesn't
|
| -// contain a value; use e.g. std::unique_ptr<T> instead if that's too
|
| -// expensive.
|
| +// Simple std::optional-wannabe. It either contains a T or not.
|
| //
|
| // A moved-from Optional<T> may only be destroyed, and assigned to if T allows
|
| // being assigned to after having been moved from. Specifically, you may not
|
| @@ -66,21 +62,90 @@ class Optional final {
|
| Optional() : has_value_(false) {}
|
|
|
| // Construct an Optional that contains a value.
|
| - explicit Optional(const T& val) : value_(val), has_value_(true) {}
|
| - explicit Optional(T&& val) : value_(std::move(val)), has_value_(true) {}
|
| + explicit Optional(const T& value) : has_value_(true) {
|
| + new (&value_) T(value);
|
| + }
|
| + explicit Optional(T&& value) : has_value_(true) {
|
| + new (&value_) T(std::move(value));
|
| + }
|
| +
|
| + // Copy constructor: copies the value from m if it has one.
|
| + Optional(const Optional& m) : has_value_(m.has_value_) {
|
| + if (has_value_)
|
| + new (&value_) T(m.value_);
|
| + }
|
| +
|
| + // Move constructor: if m has a value, moves the value from m, leaving m
|
| + // still in a state where it has a value, but a moved-from one (the
|
| + // properties of which depends on T; the only general guarantee is that we
|
| + // can destroy m).
|
| + Optional(Optional&& m) : has_value_(m.has_value_) {
|
| + if (has_value_)
|
| + new (&value_) T(std::move(m.value_));
|
| + }
|
|
|
| - // Copy and move constructors.
|
| - Optional(const Optional&) = default;
|
| - Optional(Optional&&) = default;
|
| + ~Optional() {
|
| + if (has_value_)
|
| + value_.~T();
|
| + }
|
|
|
| - // Assignment.
|
| - Optional& operator=(const Optional&) = default;
|
| - Optional& operator=(Optional&&) = default;
|
| + // Copy assignment. Uses T's copy assignment if both sides have a value, T's
|
| + // copy constructor if only the right-hand side has a value.
|
| + Optional& operator=(const Optional& m) {
|
| + if (m.has_value_) {
|
| + if (has_value_) {
|
| + value_ = m.value_; // T's copy assignment.
|
| + } else {
|
| + new (&value_) T(m.value_); // T's copy constructor.
|
| + has_value_ = true;
|
| + }
|
| + } else if (has_value_) {
|
| + value_.~T();
|
| + has_value_ = false;
|
| + }
|
| + return *this;
|
| + }
|
| +
|
| + // Move assignment. Uses T's move assignment if both sides have a value, T's
|
| + // move constructor if only the right-hand side has a value. The state of m
|
| + // after it's been moved from is as for the move constructor.
|
| + Optional& operator=(Optional&& m) {
|
| + if (m.has_value_) {
|
| + if (has_value_) {
|
| + value_ = std::move(m.value_); // T's move assignment.
|
| + } else {
|
| + new (&value_) T(std::move(m.value_)); // T's move constructor.
|
| + has_value_ = true;
|
| + }
|
| + } else if (has_value_) {
|
| + value_.~T();
|
| + has_value_ = false;
|
| + }
|
| + return *this;
|
| + }
|
|
|
| + // Swap the values if both m1 and m2 have values; move the value if only one
|
| + // of them has one.
|
| friend void swap(Optional& m1, Optional& m2) {
|
| - using std::swap;
|
| - swap(m1.value_, m2.value_);
|
| - swap(m1.has_value_, m2.has_value_);
|
| + if (m1.has_value_) {
|
| + if (m2.has_value_) {
|
| + // Both have values: swap.
|
| + using std::swap;
|
| + swap(m1.value_, m2.value_);
|
| + } else {
|
| + // Only m1 has a value: move it to m2.
|
| + new (&m2.value_) T(std::move(m1.value_));
|
| + m1.value_.~T(); // Destroy the moved-from value.
|
| + m1.has_value_ = false;
|
| + m2.has_value_ = true;
|
| + }
|
| + } else if (m2.has_value_) {
|
| + // Only m2 has a value: move it to m1.
|
| + new (&m1.value_) T(std::move(m2.value_));
|
| + m2.value_.~T(); // Destroy the moved-from value.
|
| + m1.has_value_ = true;
|
| + m2.has_value_ = false;
|
| + }
|
| }
|
|
|
| // Conversion to bool to test if we have a value.
|
| @@ -122,10 +187,15 @@ class Optional final {
|
| }
|
|
|
| private:
|
| - // Invariant: Unless *this has been moved from, value_ is default-initialized
|
| - // (or copied or moved from a default-initialized T) if !has_value_.
|
| - T value_;
|
| - bool has_value_;
|
| + bool has_value_; // True iff value_ contains a live value.
|
| + union {
|
| + // By placing value_ in a union, we get to manage its construction and
|
| + // destruction manually: the Optional constructors won't automatically
|
| + // construct it, and the Optional destructor won't automatically destroy
|
| + // it. Basically, this just allocates a properly sized and aligned block of
|
| + // memory in which we can manually put a T with placement new.
|
| + T value_;
|
| + };
|
| };
|
|
|
| } // namespace rtc
|
|
|