You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

238 lines
4.3 KiB

#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)...);
}