// Copyright 2017 The Abseil Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #ifndef ABSL_TYPES_INTERNAL_OPTIONAL_H_ #define ABSL_TYPES_INTERNAL_OPTIONAL_H_ #include <functional> #include <new> #include <type_traits> #include <utility> #include "absl/base/internal/inline_variable.h" #include "absl/memory/memory.h" #include "absl/meta/type_traits.h" #include "absl/utility/utility.h" // ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS // // Inheriting constructors is supported in GCC 4.8+, Clang 3.3+ and MSVC 2015. // __cpp_inheriting_constructors is a predefined macro and a recommended way to // check for this language feature, but GCC doesn't support it until 5.0 and // Clang doesn't support it until 3.6. // Also, MSVC 2015 has a bug: it doesn't inherit the constexpr template // constructor. For example, the following code won't work on MSVC 2015 Update3: // struct Base { // int t; // template <typename T> // constexpr Base(T t_) : t(t_) {} // }; // struct Foo : Base { // using Base::Base; // } // constexpr Foo foo(0); // doesn't work on MSVC 2015 #if defined(__clang__) #if __has_feature(cxx_inheriting_constructors) #define ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS 1 #endif #elif (defined(__GNUC__) && \ (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 8)) || \ (__cpp_inheriting_constructors >= 200802) || \ (defined(_MSC_VER) && _MSC_VER >= 1910) #define ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS 1 #endif namespace absl { ABSL_NAMESPACE_BEGIN // Forward declaration template <typename T> class optional; namespace optional_internal { // This tag type is used as a constructor parameter type for `nullopt_t`. struct init_t { explicit init_t() = default; }; struct empty_struct {}; // This class stores the data in optional<T>. // It is specialized based on whether T is trivially destructible. // This is the specialization for non trivially destructible type. template <typename T, bool unused = std::is_trivially_destructible<T>::value> class optional_data_dtor_base { struct dummy_type { static_assert(sizeof(T) % sizeof(empty_struct) == 0, ""); // Use an array to avoid GCC 6 placement-new warning. empty_struct data[sizeof(T) / sizeof(empty_struct)]; }; protected: // Whether there is data or not. bool engaged_; // Data storage union { T data_; dummy_type dummy_; }; void destruct() noexcept { if (engaged_) { data_.~T(); engaged_ = false; } } // dummy_ must be initialized for constexpr constructor. constexpr optional_data_dtor_base() noexcept : engaged_(false), dummy_{{}} {} template <typename... Args> constexpr explicit optional_data_dtor_base(in_place_t, Args&&... args) : engaged_(true), data_(absl::forward<Args>(args)...) {} ~optional_data_dtor_base() { destruct(); } }; // Specialization for trivially destructible type. template <typename T> class optional_data_dtor_base<T, true> { struct dummy_type { static_assert(sizeof(T) % sizeof(empty_struct) == 0, ""); // Use array to avoid GCC 6 placement-new warning. empty_struct data[sizeof(T) / sizeof(empty_struct)]; }; protected: // Whether there is data or not. bool engaged_; // Data storage union { T data_; dummy_type dummy_; }; void destruct() noexcept { engaged_ = false; } // dummy_ must be initialized for constexpr constructor. constexpr optional_data_dtor_base() noexcept : engaged_(false), dummy_{{}} {} template <typename... Args> constexpr explicit optional_data_dtor_base(in_place_t, Args&&... args) : engaged_(true), data_(absl::forward<Args>(args)...) {} }; template <typename T> class optional_data_base : public optional_data_dtor_base<T> { protected: using base = optional_data_dtor_base<T>; #ifdef ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS using base::base; #else optional_data_base() = default; template <typename... Args> constexpr explicit optional_data_base(in_place_t t, Args&&... args) : base(t, absl::forward<Args>(args)...) {} #endif template <typename... Args> void construct(Args&&... args) { // Use dummy_'s address to work around casting cv-qualified T* to void*. ::new (static_cast<void*>(&this->dummy_)) T(std::forward<Args>(args)...); this->engaged_ = true; } template <typename U> void assign(U&& u) { if (this->engaged_) { this->data_ = std::forward<U>(u); } else { construct(std::forward<U>(u)); } } }; // TODO(absl-team): Add another class using // std::is_trivially_move_constructible trait when available to match // http://cplusplus.github.io/LWG/lwg-defects.html#2900, for types that // have trivial move but nontrivial copy. // Also, we should be checking is_trivially_copyable here, which is not // supported now, so we use is_trivially_* traits instead. template <typename T, bool unused = absl::is_trivially_copy_constructible<T>::value&& absl::is_trivially_copy_assignable<typename std::remove_cv< T>::type>::value&& std::is_trivially_destructible<T>::value> class optional_data; // Trivially copyable types template <typename T> class optional_data<T, true> : public optional_data_base<T> { protected: #ifdef ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS using optional_data_base<T>::optional_data_base; #else optional_data() = default; template <typename... Args> constexpr explicit optional_data(in_place_t t, Args&&... args) : optional_data_base<T>(t, absl::forward<Args>(args)...) {} #endif }; template <typename T> class optional_data<T, false> : public optional_data_base<T> { protected: #ifdef ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS using optional_data_base<T>::optional_data_base; #else template <typename... Args> constexpr explicit optional_data(in_place_t t, Args&&... args) : optional_data_base<T>(t, absl::forward<Args>(args)...) {} #endif optional_data() = default; optional_data(const optional_data& rhs) : optional_data_base<T>() { if (rhs.engaged_) { this->construct(rhs.data_); } } optional_data(optional_data&& rhs) noexcept( absl::default_allocator_is_nothrow::value || std::is_nothrow_move_constructible<T>::value) : optional_data_base<T>() { if (rhs.engaged_) { this->construct(std::move(rhs.data_)); } } optional_data& operator=(const optional_data& rhs) { if (rhs.engaged_) { this->assign(rhs.data_); } else { this->destruct(); } return *this; } optional_data& operator=(optional_data&& rhs) noexcept( std::is_nothrow_move_assignable<T>::value&& std::is_nothrow_move_constructible<T>::value) { if (rhs.engaged_) { this->assign(std::move(rhs.data_)); } else { this->destruct(); } return *this; } }; // Ordered by level of restriction, from low to high. // Copyable implies movable. enum class copy_traits { copyable = 0, movable = 1, non_movable = 2 }; // Base class for enabling/disabling copy/move constructor. template <copy_traits> class optional_ctor_base; template <> class optional_ctor_base<copy_traits::copyable> { public: constexpr optional_ctor_base() = default; optional_ctor_base(const optional_ctor_base&) = default; optional_ctor_base(optional_ctor_base&&) = default; optional_ctor_base& operator=(const optional_ctor_base&) = default; optional_ctor_base& operator=(optional_ctor_base&&) = default; }; template <> class optional_ctor_base<copy_traits::movable> { public: constexpr optional_ctor_base() = default; optional_ctor_base(const optional_ctor_base&) = delete; optional_ctor_base(optional_ctor_base&&) = default; optional_ctor_base& operator=(const optional_ctor_base&) = default; optional_ctor_base& operator=(optional_ctor_base&&) = default; }; template <> class optional_ctor_base<copy_traits::non_movable> { public: constexpr optional_ctor_base() = default; optional_ctor_base(const optional_ctor_base&) = delete; optional_ctor_base(optional_ctor_base&&) = delete; optional_ctor_base& operator=(const optional_ctor_base&) = default; optional_ctor_base& operator=(optional_ctor_base&&) = default; }; // Base class for enabling/disabling copy/move assignment. template <copy_traits> class optional_assign_base; template <> class optional_assign_base<copy_traits::copyable> { public: constexpr optional_assign_base() = default; optional_assign_base(const optional_assign_base&) = default; optional_assign_base(optional_assign_base&&) = default; optional_assign_base& operator=(const optional_assign_base&) = default; optional_assign_base& operator=(optional_assign_base&&) = default; }; template <> class optional_assign_base<copy_traits::movable> { public: constexpr optional_assign_base() = default; optional_assign_base(const optional_assign_base&) = default; optional_assign_base(optional_assign_base&&) = default; optional_assign_base& operator=(const optional_assign_base&) = delete; optional_assign_base& operator=(optional_assign_base&&) = default; }; template <> class optional_assign_base<copy_traits::non_movable> { public: constexpr optional_assign_base() = default; optional_assign_base(const optional_assign_base&) = default; optional_assign_base(optional_assign_base&&) = default; optional_assign_base& operator=(const optional_assign_base&) = delete; optional_assign_base& operator=(optional_assign_base&&) = delete; }; template <typename T> struct ctor_copy_traits { static constexpr copy_traits traits = std::is_copy_constructible<T>::value ? copy_traits::copyable : std::is_move_constructible<T>::value ? copy_traits::movable : copy_traits::non_movable; }; template <typename T> struct assign_copy_traits { static constexpr copy_traits traits = absl::is_copy_assignable<T>::value && std::is_copy_constructible<T>::value ? copy_traits::copyable : absl::is_move_assignable<T>::value && std::is_move_constructible<T>::value ? copy_traits::movable : copy_traits::non_movable; }; // Whether T is constructible or convertible from optional<U>. template <typename T, typename U> struct is_constructible_convertible_from_optional : std::integral_constant< bool, std::is_constructible<T, optional<U>&>::value || std::is_constructible<T, optional<U>&&>::value || std::is_constructible<T, const optional<U>&>::value || std::is_constructible<T, const optional<U>&&>::value || std::is_convertible<optional<U>&, T>::value || std::is_convertible<optional<U>&&, T>::value || std::is_convertible<const optional<U>&, T>::value || std::is_convertible<const optional<U>&&, T>::value> {}; // Whether T is constructible or convertible or assignable from optional<U>. template <typename T, typename U> struct is_constructible_convertible_assignable_from_optional : std::integral_constant< bool, is_constructible_convertible_from_optional<T, U>::value || std::is_assignable<T&, optional<U>&>::value || std::is_assignable<T&, optional<U>&&>::value || std::is_assignable<T&, const optional<U>&>::value || std::is_assignable<T&, const optional<U>&&>::value> {}; // Helper function used by [optional.relops], [optional.comp_with_t], // for checking whether an expression is convertible to bool. bool convertible_to_bool(bool); // Base class for std::hash<absl::optional<T>>: // If std::hash<std::remove_const_t<T>> is enabled, it provides operator() to // compute the hash; Otherwise, it is disabled. // Reference N4659 23.14.15 [unord.hash]. template <typename T, typename = size_t> struct optional_hash_base { optional_hash_base() = delete; optional_hash_base(const optional_hash_base&) = delete; optional_hash_base(optional_hash_base&&) = delete; optional_hash_base& operator=(const optional_hash_base&) = delete; optional_hash_base& operator=(optional_hash_base&&) = delete; }; template <typename T> struct optional_hash_base<T, decltype(std::hash<absl::remove_const_t<T> >()( std::declval<absl::remove_const_t<T> >()))> { using argument_type = absl::optional<T>; using result_type = size_t; size_t operator()(const absl::optional<T>& opt) const { absl::type_traits_internal::AssertHashEnabled<absl::remove_const_t<T>>(); if (opt) { return std::hash<absl::remove_const_t<T> >()(*opt); } else { return static_cast<size_t>(0x297814aaad196e6dULL); } } }; } // namespace optional_internal ABSL_NAMESPACE_END } // namespace absl #undef ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS #endif // ABSL_TYPES_INTERNAL_OPTIONAL_H_