#pragma once #include <type_traits> #include <assert.h> #include <stdexcept> struct in_place_t { explicit in_place_t() = default; }; template<typename BaseType> class optional { using Storage = typename std::aligned_storage<sizeof(BaseType), std::alignment_of<BaseType>::value>::type; Storage m_storage; bool m_valid = false; public: typedef optional<BaseType> self_type; optional() = default; ~optional() { reset(); } optional(const optional& src) : m_valid(src.m_valid) { if (src.m_valid) { new(&m_storage) BaseType(*src); } } optional(optional&& src) : m_valid(src.m_valid) { if (src.m_valid) { new(&m_storage) BaseType(std::move(*src)); } } template<typename U = BaseType, typename = typename std::enable_if< std::is_constructible<BaseType, U&&>::value && !(std::is_same<typename std::decay<U>::type, self_type>::value) >::type > optional(U&& value) : m_valid(true) { new(&m_storage) BaseType(std::forward<U>(value)); } template<typename... Args, typename = typename std::enable_if<std::is_constructible<BaseType, Args...>::value>::type> explicit optional(in_place_t tag, Args&&... args) : m_valid(true) { new(&m_storage) BaseType(std::forward<Args>(args)...); } optional& operator = (optional&& src) noexcept { if (src.m_valid) { if (m_valid) { **this = std::move(*src); } else { new(&m_storage) BaseType(std::move(*src)); m_valid = true; } } else if (m_valid) { reinterpret_cast<BaseType*>(&m_storage)->~BaseType(); m_valid = false; } return *this; } optional& operator = (const optional& src) { if (src.m_valid) { if (m_valid) { **this = *src; } else { new(&m_storage) BaseType(*src); m_valid = true; } } else if (m_valid) { reinterpret_cast<BaseType*>(&m_storage)->~BaseType(); m_valid = false; } return *this; } template<typename U = BaseType, typename = typename std::enable_if< !std::is_same<typename std::decay<U>::type, self_type>::value && std::is_constructible<BaseType, U>::value && std::is_assignable<BaseType&, U>::value >::type> optional& operator = (U&& value) { if (m_valid) { **this = std::forward<U>(value); } else { new(&m_storage) BaseType(std::forward<U>(value)); m_valid = true; } return *this; } void reset() { if (m_valid) { (**this).BaseType::~BaseType(); m_valid = false; } } constexpr explicit operator bool() const { return m_valid; } constexpr bool has_value() const { return m_valid; } constexpr bool valid() const { return has_value(); } BaseType& operator * () & { assert(m_valid); return *reinterpret_cast<BaseType*>(&m_storage); } const BaseType& operator * () const & { assert(m_valid); return *reinterpret_cast<const BaseType*>(&m_storage); } BaseType&& operator * () && { assert(m_valid); return std::move(*reinterpret_cast<BaseType*>(&m_storage)); } const BaseType&& operator * () const && { assert(m_valid); return std::move(*reinterpret_cast<BaseType*>(&m_storage)); } BaseType* operator -> () { assert(m_valid); return reinterpret_cast<BaseType*>(&m_storage); } const BaseType* operator -> () const { assert(m_valid); return reinterpret_cast<const BaseType*>(&m_storage); } BaseType& value() { if (!has_value()) { throw std::logic_error("bad_optional_access"); } return **this; } const BaseType& value() const { if (!has_value()) { throw std::logic_error("bad_optional_access"); } return **this; } template<typename U> BaseType value_or(U&& default_value) && { return has_value() ? std::move(**this) : static_cast<BaseType>(std::forward<U>(default_value)); } template<typename U> const BaseType value_or(U&& default_value) const & { return has_value() ? **this : static_cast<BaseType>(std::forward<U>(default_value)); } template<typename... Args> BaseType& emplace(Args&&... args) { reset(); new(&m_storage) BaseType(std::forward<Args>(args)...); m_valid = true; return **this; } }; template<typename T> optional<typename std::decay<T>::type> make_optional(T&& val) { return optional<typename std::decay<T>::type>(std::forward<T>(val)); } template<typename T, typename... Args> optional<T> make_optional(Args&&... args) { return optional<T>(in_place_t{}, std::forward<Args>(args)...); }