#pragma once #include "gp_config.hpp" #include "gp/functional/bind_front.hpp" #include "gp/functional/monostates.hpp" #include "gp/algorithms/move.hpp" #include "gp/exception.hpp" #include #include namespace gp{ template class optional final{ bool ready = false; alignas(T) char buffer[sizeof(T)]; public: constexpr optional() : ready{false} {} constexpr optional(nullopt_t) : ready{false} {} constexpr optional(T& value) : ready{true} { new(buffer) T(value); } constexpr optional(T&& value) : ready{true} { new(buffer) T(gp::move(value)); } constexpr optional(const optional& oth) : ready{oth.ready} { if(ready) { new(buffer) T(oth.value()); } } constexpr optional(optional&& oth) : ready{oth.ready} { if(ready) { new(buffer) T(gp::move(oth.value())); } } ~optional() { if(ready) { ((T*)buffer)->~T(); } } optional& operator=(nullopt_t) { if(ready) { ((T*)buffer)->~T(); ready = false; } return *this; } optional& operator=(T& value) { if(ready) { *(T*)buffer = value; } else { ready = true; new(buffer) T(value); } return *this; } optional& operator=(T&& value) { if(ready) { *(T*)buffer = gp::move(value); } else { ready = true; new(buffer) T(gp::move(value)); } return *this; } optional& operator=(const optional& oth) { if(ready) { *(T*)buffer = oth.value(); } else { ready = true; new(buffer) T(oth.value()); } return *this; } optional& operator=(optional&& oth) { if(ready) { *(T*)buffer = gp::move(oth.value()); } else { ready = true; new(buffer) T(gp::move(oth.value())); } return *this; } constexpr bool has_value() const { return ready; } constexpr T& value() { if constexpr (gp_config::has_exceptions) { if(!ready) { throw bad_optional{}; } } else { gp_config::assertion(ready, "bad optional access"); } return *reinterpret_cast(buffer); } constexpr const T& value() const { if constexpr (gp_config::has_exceptions) { if(!ready) { throw bad_optional{}; } } else { gp_config::assertion(ready, "bad optional access"); } return *reinterpret_cast(buffer); } }; }