405 lines
13 KiB
C++
405 lines
13 KiB
C++
// 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_` must be initialized if `engaged_` is true.
|
|
#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0)
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
|
|
#endif
|
|
data_.~T();
|
|
#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0)
|
|
#pragma GCC diagnostic pop
|
|
#endif
|
|
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_
|