Chromium Code Reviews| Index: base/optional.h |
| diff --git a/base/optional.h b/base/optional.h |
| index 5ae427f8a9cc2d79b65665fb6f103b15b2230295..1a97795465b5548cbf66f8fbd1557418c6c25e15 100644 |
| --- a/base/optional.h |
| +++ b/base/optional.h |
| @@ -9,6 +9,7 @@ |
| #include "base/logging.h" |
| #include "base/memory/aligned_memory.h" |
| +#include "base/template_util.h" |
| namespace base { |
| @@ -30,6 +31,29 @@ constexpr in_place_t in_place = {}; |
| // http://en.cppreference.com/w/cpp/utility/optional/nullopt |
| constexpr nullopt_t nullopt(0); |
| +namespace internal { |
| + |
| +template <typename T, bool = base::is_trivially_destructible<T>::value> |
| +struct OptionalStorage { |
| + ~OptionalStorage() { |
| + if (!is_null_) |
| + buffer_.template data_as<T>()->~T(); |
| + } |
| + |
| + bool is_null_ = true; |
| + base::AlignedMemory<sizeof(T), ALIGNOF(T)> buffer_; |
| +}; |
| + |
| +template <typename T> |
| +struct OptionalStorage<T, true> { |
| + ~OptionalStorage() = default; |
|
danakj
2016/05/25 20:02:08
Can you leave a comment here explaining that this
alshabalin
2016/05/26 09:18:45
Done.
|
| + |
| + bool is_null_ = true; |
| + base::AlignedMemory<sizeof(T), ALIGNOF(T)> buffer_; |
| +}; |
| + |
| +} // namespace internal |
| + |
| // base::Optional is a Chromium version of the C++17 optional class: |
| // std::optional documentation: |
| // http://en.cppreference.com/w/cpp/utility/optional |
| @@ -46,16 +70,18 @@ constexpr nullopt_t nullopt(0); |
| template <typename T> |
| class Optional { |
| public: |
| + using value_type = T; |
| + |
| constexpr Optional() = default; |
| Optional(base::nullopt_t) : Optional() {} |
| Optional(const Optional& other) { |
| - if (!other.is_null_) |
| + if (!other.storage_.is_null_) |
| Init(other.value()); |
| } |
| Optional(Optional&& other) { |
| - if (!other.is_null_) |
| + if (!other.storage_.is_null_) |
| Init(std::move(other.value())); |
| } |
| @@ -68,10 +94,7 @@ class Optional { |
| emplace(std::forward<Args>(args)...); |
| } |
| - ~Optional() { |
| - // TODO(mlamouri): use is_trivially_destructible<T>::value when possible. |
| - FreeIfNeeded(); |
| - } |
| + ~Optional() = default; |
| Optional& operator=(base::nullopt_t) { |
| FreeIfNeeded(); |
| @@ -79,7 +102,7 @@ class Optional { |
| } |
| Optional& operator=(const Optional& other) { |
| - if (other.is_null_) { |
| + if (other.storage_.is_null_) { |
| FreeIfNeeded(); |
| return *this; |
| } |
| @@ -89,7 +112,7 @@ class Optional { |
| } |
| Optional& operator=(Optional&& other) { |
| - if (other.is_null_) { |
| + if (other.storage_.is_null_) { |
| FreeIfNeeded(); |
| return *this; |
| } |
| @@ -108,14 +131,14 @@ class Optional { |
| // TODO(mlamouri): can't use 'constexpr' with DCHECK. |
| const T* operator->() const { |
| - DCHECK(!is_null_); |
| + DCHECK(!storage_.is_null_); |
| return &value(); |
| } |
| // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was |
| // meant to be 'constexpr const'. |
| T* operator->() { |
| - DCHECK(!is_null_); |
| + DCHECK(!storage_.is_null_); |
| return &value(); |
| } |
| @@ -131,32 +154,32 @@ class Optional { |
| // meant to be 'constexpr const'. |
| T&& operator*() && { return std::move(value()); } |
| - constexpr explicit operator bool() const { return !is_null_; } |
| + constexpr explicit operator bool() const { return !storage_.is_null_; } |
| // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was |
| // meant to be 'constexpr const'. |
| T& value() & { |
| - DCHECK(!is_null_); |
| - return *buffer_.template data_as<T>(); |
| + DCHECK(!storage_.is_null_); |
| + return *storage_.buffer_.template data_as<T>(); |
| } |
| // TODO(mlamouri): can't use 'constexpr' with DCHECK. |
| const T& value() const& { |
| - DCHECK(!is_null_); |
| - return *buffer_.template data_as<T>(); |
| + DCHECK(!storage_.is_null_); |
| + return *storage_.buffer_.template data_as<T>(); |
| } |
| // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was |
| // meant to be 'constexpr const'. |
| T&& value() && { |
| - DCHECK(!is_null_); |
| - return std::move(*buffer_.template data_as<T>()); |
| + DCHECK(!storage_.is_null_); |
| + return std::move(*storage_.buffer_.template data_as<T>()); |
| } |
| // TODO(mlamouri): can't use 'constexpr' with DCHECK. |
| const T&& value() const&& { |
| - DCHECK(!is_null_); |
| - return std::move(*buffer_.template data_as<T>()); |
| + DCHECK(!storage_.is_null_); |
| + return std::move(*storage_.buffer_.template data_as<T>()); |
| } |
| template <class U> |
| @@ -166,7 +189,8 @@ class Optional { |
| // "T must be copy constructible"); |
| static_assert(std::is_convertible<U, T>::value, |
| "U must be convertible to T"); |
| - return is_null_ ? static_cast<T>(std::forward<U>(default_value)) : value(); |
| + return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value)) |
| + : value(); |
| } |
| template <class U> |
| @@ -176,26 +200,26 @@ class Optional { |
| // "T must be move constructible"); |
| static_assert(std::is_convertible<U, T>::value, |
| "U must be convertible to T"); |
| - return is_null_ ? static_cast<T>(std::forward<U>(default_value)) |
| - : std::move(value()); |
| + return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value)) |
| + : std::move(value()); |
| } |
| void swap(Optional& other) { |
| - if (is_null_ && other.is_null_) |
| + if (storage_.is_null_ && other.storage_.is_null_) |
| return; |
| - if (is_null_ != other.is_null_) { |
| - if (is_null_) { |
| - Init(std::move(*other.buffer_.template data_as<T>())); |
| + if (storage_.is_null_ != other.storage_.is_null_) { |
| + if (storage_.is_null_) { |
| + Init(std::move(*other.storage_.buffer_.template data_as<T>())); |
| other.FreeIfNeeded(); |
| } else { |
| - other.Init(std::move(*buffer_.template data_as<T>())); |
| + other.Init(std::move(*storage_.buffer_.template data_as<T>())); |
| FreeIfNeeded(); |
| } |
| return; |
| } |
| - DCHECK(!is_null_ && !other.is_null_); |
| + DCHECK(!storage_.is_null_ && !other.storage_.is_null_); |
| using std::swap; |
| swap(**this, *other); |
| } |
| @@ -208,47 +232,46 @@ class Optional { |
| private: |
| void Init(const T& value) { |
| - DCHECK(is_null_); |
| - new (buffer_.template data_as<T>()) T(value); |
| - is_null_ = false; |
| + DCHECK(storage_.is_null_); |
| + new (storage_.buffer_.template data_as<T>()) T(value); |
| + storage_.is_null_ = false; |
| } |
| void Init(T&& value) { |
| - DCHECK(is_null_); |
| - new (buffer_.template data_as<T>()) T(std::move(value)); |
| - is_null_ = false; |
| + DCHECK(storage_.is_null_); |
| + new (storage_.buffer_.template data_as<T>()) T(std::move(value)); |
| + storage_.is_null_ = false; |
| } |
| template <class... Args> |
| void Init(Args&&... args) { |
| - DCHECK(is_null_); |
| - new (buffer_.template data_as<T>()) T(std::forward<Args>(args)...); |
| - is_null_ = false; |
| + DCHECK(storage_.is_null_); |
| + new (storage_.buffer_.template data_as<T>()) T(std::forward<Args>(args)...); |
| + storage_.is_null_ = false; |
| } |
| void InitOrAssign(const T& value) { |
| - if (is_null_) |
| + if (storage_.is_null_) |
| Init(value); |
| else |
| - *buffer_.template data_as<T>() = value; |
| + *storage_.buffer_.template data_as<T>() = value; |
| } |
| void InitOrAssign(T&& value) { |
| - if (is_null_) |
| + if (storage_.is_null_) |
| Init(std::move(value)); |
| else |
| - *buffer_.template data_as<T>() = std::move(value); |
| + *storage_.buffer_.template data_as<T>() = std::move(value); |
| } |
| void FreeIfNeeded() { |
| - if (is_null_) |
| + if (storage_.is_null_) |
| return; |
| - buffer_.template data_as<T>()->~T(); |
| - is_null_ = true; |
| + storage_.buffer_.template data_as<T>()->~T(); |
| + storage_.is_null_ = true; |
| } |
| - bool is_null_ = true; |
| - base::AlignedMemory<sizeof(T), ALIGNOF(T)> buffer_; |
| + internal::OptionalStorage<T> storage_; |
| }; |
| template <class T> |