I'm preparing for entry-level C++ developer interview and decided to implement some of std:: members. Here's my implementation of std::optional
. I would be grateful for any feedback to help me improve my implementation.
#include <memory> #include <type_traits> #include <utility> #include <cassert> template<typename T> struct optional { optional() noexcept : _engaged(false) {} optional(T&& value) noexcept : _value(std::move(value)), _engaged(true) {} optional(optional&& rhs) noexcept : _engaged(std::exchange(rhs._engaged, false)) { if (_engaged) { std::construct_at(std::addressof(_value), std::move(rhs._value)); } } optional& operator=(optional&& rhs) noexcept { if (this != &rhs) { if (rhs._engaged) { if (_engaged) { _value = std::move(rhs._value); } else { std::construct_at(std::addressof(_value), std::move(rhs._value)); _engaged = true; } } else { reset(); } rhs.reset(); } return *this; } optional(const optional& rhs) noexcept : _engaged(rhs._engaged) { if (_engaged) { std::construct_at(std::addressof(_value), rhs._value); } } optional& operator=(const optional& rhs) noexcept { if (this != &rhs) { if (rhs._engaged) { if (_engaged) { _value = rhs._value; } else { std::construct_at(std::addressof(_value), rhs._value); _engaged = true; } } else { reset(); } } return *this; } ~optional() noexcept { reset(); } [[nodiscard]] T& operator*() noexcept { assert(_engaged && "Attempting to access a disengaged optional!"); return _value; } [[nodiscard]] const T& operator*() const noexcept { assert(_engaged && "Attempting to access a disengaged optional!"); return _value; } [[nodiscard]] T* operator->() noexcept { assert(_engaged && "Attempting to dereference a disengaged optional!"); return std::addressof(_value); } [[nodiscard]] const T* operator->() const noexcept { assert(_engaged && "Attempting to dereference a disengaged optional!"); return std::addressof(_value); } operator bool() const noexcept { return _engaged; } bool has_value() const noexcept { return _engaged; } template<typename... Args> T& emplace(Args... args) { reset(); std::construct_at(std::addressof(_value), std::forward<Args>(args)...); _engaged = true; return _value; } template<typename U> [[nodiscard]] T value_or(U&& other_value) const & noexcept requires(std::is_copy_constructible_v<T> && std::is_convertible_v<U&&, T>) { return _engaged ? _value : static_cast<T>(std::forward<U>(other_value)); } template<typename U> [[nodiscard]] T value_or(U&& other_value) const && noexcept requires(std::is_move_constructible_v<T> && std::is_convertible_v<U&&, T>) { return _engaged ? std::move(_value) : static_cast<T>(std::forward<U>(other_value)); } T& value() & noexcept { return _value; } T&& value() && noexcept { return std::move(_value); } void reset() noexcept { if (_engaged) { if constexpr (!std::is_trivially_destructible_v<T>) { _value.~T(); } _engaged = false; } } private: union { std::byte _null_state; T _value; }; bool _engaged; };