Propagate triviality, conditionally delete special members

This commit is contained in:
Simon Brand
2017-11-01 15:43:58 +00:00
parent 9a6f22d811
commit 3720227b2d

View File

@@ -268,8 +268,13 @@ struct optional_storage_base {
TL_OPTIONAL_11_CONSTEXPR optional_storage_base() noexcept TL_OPTIONAL_11_CONSTEXPR optional_storage_base() noexcept
: m_dummy(), m_has_value(false) {} : m_dummy(), m_has_value(false) {}
optional_storage_base(const optional_storage_base &) = default;
optional_storage_base(optional_storage_base &&) noexcept = default;
optional_storage_base &operator=(const optional_storage_base &) = default;
optional_storage_base &operator=(optional_storage_base &&) noexcept = default;
template <class... U> template <class... U>
TL_OPTIONAL_11_CONSTEXPR optional_storage_base(in_place_t, U &&... u) noexcept TL_OPTIONAL_11_CONSTEXPR optional_storage_base(in_place_t, U &&... u)
: m_value(std::forward<U>(u)...), m_has_value(true) {} : m_value(std::forward<U>(u)...), m_has_value(true) {}
~optional_storage_base() { ~optional_storage_base() {
@@ -292,8 +297,13 @@ template <class T> struct optional_storage_base<T, true> {
TL_OPTIONAL_11_CONSTEXPR optional_storage_base() noexcept TL_OPTIONAL_11_CONSTEXPR optional_storage_base() noexcept
: m_dummy(), m_has_value(false) {} : m_dummy(), m_has_value(false) {}
optional_storage_base(const optional_storage_base &) = default;
optional_storage_base(optional_storage_base &&) noexcept = default;
optional_storage_base &operator=(const optional_storage_base &) = default;
optional_storage_base &operator=(optional_storage_base &&) noexcept = default;
template <class... U> template <class... U>
TL_OPTIONAL_11_CONSTEXPR optional_storage_base(in_place_t, U &&... u) noexcept TL_OPTIONAL_11_CONSTEXPR optional_storage_base(in_place_t, U &&... u)
: m_value(std::forward<U>(u)...), m_has_value(true) {} : m_value(std::forward<U>(u)...), m_has_value(true) {}
~optional_storage_base() = default; ~optional_storage_base() = default;
@@ -307,6 +317,225 @@ template <class T> struct optional_storage_base<T, true> {
bool m_has_value = false; bool m_has_value = false;
}; };
template <class T> struct optional_operations_base : optional_storage_base<T> {
using optional_storage_base<T>::optional_storage_base;
void reset() noexcept {
if (this->m_has_value) {
get().~T();
this->m_has_value = false;
}
}
template <class... Args> void construct(Args &&... args) noexcept {
new (std::addressof(this->m_value)) T(std::forward<Args>(args)...);
this->m_has_value = true;
}
template <class Opt> void assign(Opt &&rhs) {
if (this->m_has_value) {
if (rhs.m_has_value) {
get() = std::forward<Opt>(rhs).get();
}
}
}
TL_OPTIONAL_11_CONSTEXPR T &get() & { return this->m_value; }
TL_OPTIONAL_11_CONSTEXPR const T &get() const & { return this->m_value; }
TL_OPTIONAL_11_CONSTEXPR T &&get() && { std::move(this->m_value); }
#ifndef TL_OPTIONAL_NO_CONSTRR
constexpr const T &&get() const && { return std::move(this->m_value); }
#endif
};
template <class T, bool = std::is_trivially_copy_constructible<T>::value>
struct optional_copy_base : optional_operations_base<T> {
using optional_operations_base<T>::optional_operations_base;
};
template <class T>
struct optional_copy_base<T, false> : optional_operations_base<T> {
using optional_operations_base<T>::optional_operations_base;
optional_copy_base() = default;
optional_copy_base(const optional_copy_base &rhs) {
if (rhs.m_has_value) {
this->construct(rhs.get());
} else {
this->reset();
}
}
optional_copy_base(optional_copy_base &&rhs) = default;
optional_copy_base &operator=(const optional_copy_base &rhs) = default;
optional_copy_base &operator=(optional_copy_base &&rhs) = default;
};
template <class T, bool = std::is_trivially_move_constructible<T>::value>
struct optional_move_base : optional_copy_base<T> {
using optional_copy_base<T>::optional_copy_base;
};
template <class T> struct optional_move_base<T, false> : optional_copy_base<T> {
using optional_copy_base<T>::optional_copy_base;
optional_move_base() = default;
optional_move_base(const optional_move_base &rhs) = default;
optional_move_base(optional_move_base &&rhs) noexcept(
std::is_nothrow_move_constructible<T>::value) {
if (rhs.m_has_value) {
this->construct(std::move(rhs.get()));
} else {
this->reset();
}
}
optional_move_base &operator=(const optional_move_base &rhs) = default;
optional_move_base &operator=(optional_move_base &&rhs) = default;
};
template <class T, bool = std::is_trivially_copy_constructible<T>::value>
struct optional_copy_assign_base : optional_move_base<T> {
using optional_move_base<T>::optional_move_base;
};
template <class T>
struct optional_copy_assign_base<T, false> : optional_move_base<T> {
using optional_move_base<T>::optional_move_base;
optional_copy_assign_base() = default;
optional_copy_assign_base(const optional_copy_assign_base &rhs) = default;
optional_copy_assign_base(optional_copy_assign_base &&rhs) = default;
optional_copy_assign_base &operator=(const optional_copy_assign_base &rhs) {
if (rhs.m_has_value) {
this->assign(rhs.get());
} else {
this->reset();
}
}
optional_copy_assign_base &
operator=(optional_copy_assign_base &&rhs) = default;
};
template <class T, bool = std::is_trivially_copy_constructible<T>::value>
struct optional_move_assign_base : optional_copy_assign_base<T> {
using optional_copy_assign_base<T>::optional_copy_assign_base;
};
template <class T>
struct optional_move_assign_base<T, false> : optional_copy_assign_base<T> {
using optional_copy_assign_base<T>::optional_copy_assign_base;
optional_move_assign_base() = default;
optional_move_assign_base(const optional_move_assign_base &rhs) = default;
optional_move_assign_base(optional_move_assign_base &&rhs) = default;
optional_move_assign_base &
operator=(const optional_move_assign_base &rhs) noexcept(
std::is_nothrow_move_constructible<T>::value
&&std::is_nothrow_move_assignable<T>::value) {
if (rhs.m_has_value) {
this->assign(std::move(rhs.get()));
} else {
this->reset();
}
}
optional_move_assign_base &
operator=(optional_move_assign_base &&rhs) = default;
};
template <class T, bool EnableCopy = std::is_copy_constructible<T>::value,
bool EnableMove = std::is_move_constructible<T>::value>
struct optional_delete_ctor_base {
optional_delete_ctor_base() = default;
optional_delete_ctor_base(const optional_delete_ctor_base &) = default;
optional_delete_ctor_base(optional_delete_ctor_base &&) noexcept = default;
optional_delete_ctor_base &
operator=(const optional_delete_ctor_base &) = default;
optional_delete_ctor_base &
operator=(optional_delete_ctor_base &&) noexcept = default;
};
template <class T> struct optional_delete_ctor_base<T, true, false> {
optional_delete_ctor_base() = default;
optional_delete_ctor_base(const optional_delete_ctor_base &) = default;
optional_delete_ctor_base(optional_delete_ctor_base &&) noexcept = delete;
optional_delete_ctor_base &
operator=(const optional_delete_ctor_base &) = default;
optional_delete_ctor_base &
operator=(optional_delete_ctor_base &&) noexcept = default;
};
template <class T> struct optional_delete_ctor_base<T, false, true> {
optional_delete_ctor_base() = default;
optional_delete_ctor_base(const optional_delete_ctor_base &) = delete;
optional_delete_ctor_base(optional_delete_ctor_base &&) noexcept = default;
optional_delete_ctor_base &
operator=(const optional_delete_ctor_base &) = default;
optional_delete_ctor_base &
operator=(optional_delete_ctor_base &&) noexcept = default;
};
template <class T> struct optional_delete_ctor_base<T, false, false> {
optional_delete_ctor_base() = default;
optional_delete_ctor_base(const optional_delete_ctor_base &) = delete;
optional_delete_ctor_base(optional_delete_ctor_base &&) noexcept = delete;
optional_delete_ctor_base &
operator=(const optional_delete_ctor_base &) = default;
optional_delete_ctor_base &
operator=(optional_delete_ctor_base &&) noexcept = default;
};
template <class T,
bool EnableCopy = (std::is_copy_constructible<T>::value &&
std::is_copy_assignable<T>::value),
bool EnableMove = (std::is_move_constructible<T>::value &&
std::is_move_assignable<T>::value)>
struct optional_delete_assign_base {
optional_delete_assign_base() = default;
optional_delete_assign_base(const optional_delete_assign_base &) = default;
optional_delete_assign_base(optional_delete_assign_base &&) noexcept =
default;
optional_delete_assign_base &
operator=(const optional_delete_assign_base &) = default;
optional_delete_assign_base &
operator=(optional_delete_assign_base &&) noexcept = default;
};
template <class T> struct optional_delete_assign_base<T, true, false> {
optional_delete_assign_base() = default;
optional_delete_assign_base(const optional_delete_assign_base &) = default;
optional_delete_assign_base(optional_delete_assign_base &&) noexcept =
default;
optional_delete_assign_base &
operator=(const optional_delete_assign_base &) = default;
optional_delete_assign_base &
operator=(optional_delete_assign_base &&) noexcept = delete;
};
template <class T> struct optional_delete_assign_base<T, false, true> {
optional_delete_assign_base() = default;
optional_delete_assign_base(const optional_delete_assign_base &) = default;
optional_delete_assign_base(optional_delete_assign_base &&) noexcept =
default;
optional_delete_assign_base &
operator=(const optional_delete_assign_base &) = delete;
optional_delete_assign_base &
operator=(optional_delete_assign_base &&) noexcept = default;
};
template <class T> struct optional_delete_assign_base<T, false, false> {
optional_delete_assign_base() = default;
optional_delete_assign_base(const optional_delete_assign_base &) = default;
optional_delete_assign_base(optional_delete_assign_base &&) noexcept =
default;
optional_delete_assign_base &
operator=(const optional_delete_assign_base &) = delete;
optional_delete_assign_base &
operator=(optional_delete_assign_base &&) noexcept = delete;
};
} // namespace detail } // namespace detail
/// \brief A tag type to represent an empty optional /// \brief A tag type to represent an empty optional
@@ -338,8 +567,11 @@ public:
/// and may be destroyed before the optional object has been destroyed. The /// and may be destroyed before the optional object has been destroyed. The
/// initialization state of the contained object is tracked by the optional /// initialization state of the contained object is tracked by the optional
/// object. /// object.
template <class T> class optional : private detail::optional_storage_base<T> { template <class T>
using base = detail::optional_storage_base<T>; class optional : private detail::optional_move_assign_base<T>,
private detail::optional_delete_ctor_base<T>,
private detail::optional_delete_assign_base<T> {
using base = detail::optional_move_assign_base<T>;
public: public:
// The different versions for C++14 and 11 are needed because deduced return // The different versions for C++14 and 11 are needed because deduced return
@@ -753,25 +985,14 @@ public:
/// ///
/// If `rhs` contains a value, the stored value is direct-initialized with it. /// If `rhs` contains a value, the stored value is direct-initialized with it.
/// Otherwise, the constructed optional is empty. /// Otherwise, the constructed optional is empty.
TL_OPTIONAL_11_CONSTEXPR optional(const optional &rhs) { TL_OPTIONAL_11_CONSTEXPR optional(const optional &rhs) = default;
if (rhs.has_value()) {
this->m_has_value = true;
new (std::addressof(this->m_value)) T(*rhs);
}
}
// TODO conditionally disable // TODO conditionally disable
/// Move constructor /// Move constructor
/// ///
/// If `rhs` contains a value, the stored value is direct-initialized with it. /// If `rhs` contains a value, the stored value is direct-initialized with it.
/// Otherwise, the constructed optional is empty. /// Otherwise, the constructed optional is empty.
TL_OPTIONAL_11_CONSTEXPR optional(optional &&rhs) noexcept( TL_OPTIONAL_11_CONSTEXPR optional(optional &&rhs) = default;
std::is_nothrow_move_constructible<T>::value) {
if (rhs.has_value()) {
this->m_has_value = true;
new (std::addressof(this->m_value)) T(std::move(*rhs));
}
}
/// Constructs the stored value in-place using the given arguments. /// Constructs the stored value in-place using the given arguments.
/// \group in_place /// \group in_place
@@ -864,53 +1085,19 @@ public:
return *this; return *this;
} }
// TODO conditionally delete, check exception guarantee
/// Copy assignment. /// Copy assignment.
/// ///
/// Copies the value from `rhs` if there is one. Otherwise resets the stored /// Copies the value from `rhs` if there is one. Otherwise resets the stored
/// value in `*this`. /// value in `*this`.
optional &operator=(const optional &rhs) { optional &operator=(const optional &rhs) = default;
if (has_value()) {
if (rhs.has_value()) {
this->m_value = rhs.m_value;
} else {
this->m_value.~T();
this->m_has_value = false;
}
}
if (rhs.has_value()) {
new (std::addressof(this->m_value)) T(rhs.m_value);
this->m_has_value = true;
}
return *this;
}
// TODO conditionally delete, check exception guarantee
/// Move assignment. /// Move assignment.
/// ///
/// Moves the value from `rhs` if there is one. Otherwise resets the stored /// Moves the value from `rhs` if there is one. Otherwise resets the stored
/// value in `*this`. /// value in `*this`.
optional &operator=(optional &&rhs) noexcept( optional &operator=(optional &&rhs) noexcept(
std::is_nothrow_move_assignable<T>::value std::is_nothrow_move_assignable<T>::value
&&std::is_nothrow_move_constructible<T>::value) { &&std::is_nothrow_move_constructible<T>::value) = default;
if (has_value()) {
if (rhs.has_value()) {
this->m_value = std::move(rhs.m_value);
} else {
this->m_value.~T();
this->m_has_value = false;
}
}
if (rhs.has_value()) {
new (std::addressof(this->m_value)) T(std::move(rhs.m_value));
this->m_has_value = true;
}
return *this;
}
// TODO conditionally delete, check exception guarantee // TODO conditionally delete, check exception guarantee
/// Assigns the stored value from `u`, destroying the old value if there was /// Assigns the stored value from `u`, destroying the old value if there was
@@ -983,7 +1170,7 @@ public:
"T must be constructible with Args"); "T must be constructible with Args");
*this = nullopt; *this = nullopt;
new (std::addressof(this->m_value)) T(std::forward<Args>(args)...); this->construct(std::forward<Args>(args)...);
} }
/// \group emplace /// \group emplace
@@ -995,7 +1182,7 @@ public:
T &> T &>
emplace(std::initializer_list<U> il, Args &&... args) { emplace(std::initializer_list<U> il, Args &&... args) {
*this = nullopt; *this = nullopt;
new (std::addressof(this->m_value)) T(il, std::forward<Args>(args)...); this->construct(il, std::forward<Args>(args)...);
} }
/// Swaps this optional with the other. /// Swaps this optional with the other.
@@ -1118,7 +1305,7 @@ public:
this->m_has_value = false; this->m_has_value = false;
} }
} }
}; }; // namespace tl
/// \group relop /// \group relop
/// \brief Compares two optional objects /// \brief Compares two optional objects