#include <cassert>
#include <memory>
#include <utility>
#include <string>
#include <vector>

template <typename T>
class Optional
{
public:

    Optional()
        : valid { false }
    {
    }

    Optional(T const& initial)
        : valid { true }
    {
        new (&data[0]) T { initial };
    }

    Optional(Optional<T> const& other)
        : valid { other.valid }
    {
        if (other.valid)
        {
            set_value(other.get_value());
        }
    }

    ~Optional()
    {
        clear();
    }

    Optional<T>& operator=(Optional<T> const& other)
    {
        if (valid && other.valid)
        {
            get_value() = other.get_value();
        }
        else if (other.valid)
        {
            set_value(other.get_value());
        }
        else if (valid)
        {
            clear();
        }
        return *this;
    }

    Optional<T>& operator=(T const& new_value)
    {
        if (valid)
        {
            get_value() = new_value;
        }
        else
        {
            set_value(new_value);
        }
        return *this;
    }

    void clear()
    {
        if (valid)
        {
            // call destructor
            std::destroy_at(&get_value());

            // set optional into empty state
            valid = false;
        }
    }

    bool is_valid() const
    {
        return valid;
    }

    T& get_value()
    {
        return *std::launder(reinterpret_cast<T*>(&data[0]));
    }

    T const& get_value() const
    {
        return *std::launder(reinterpret_cast<T const*>(&data[0]));
    }

private:

    void set_value(T const& new_value)
    {
        new (&data[0]) T { new_value };
        valid = true;
    }

    char data[sizeof(T)];
    bool valid;

};

int main()
{
    {
        Optional<int> o { };
        assert( !o.is_valid() );

        o = 5;
        assert( o.is_valid() );
        assert( o.get_value() == 5 );

        // test that a reference is returned
        o.get_value() = 3;
        assert( o.get_value() == 3 );

        o.clear();
        assert( !o.is_valid() );
    }

    {
        // save a large std::string
        Optional<std::string> o { std::string(1000, '-') };

        assert( o.is_valid() );
        assert(( o.get_value() == std::string(1000, '-') ));

        auto previous_capacity = o.get_value().capacity();

        // here the destructor of the stored string must be called...
        o.clear();
        assert( !o.is_valid() );

        o = "";
        assert( o.is_valid() );
        assert( o.get_value().empty() );

        // ... due to the destructor of the string having been called previously
        // the number of allocated bytes in the new string must be different.
        //
        // If this test fails that means you are still re-using the same string
        // object as the beginning of the testcase, this is incorrect.
        assert( o.get_value().capacity() != previous_capacity );
    }

    // test copy operations
    {
        Optional<std::vector<int>> o1 { std::vector<int> { 1, 2, 3 } };
        Optional<std::vector<int>> o2 { };

        Optional<std::vector<int>> o3 { o1 }; // copy o1
        Optional<std::vector<int>> o4 { o2 }; // copy o2

        assert( o3.is_valid() );
        assert( o3.get_value() == o1.get_value() );

        // make sure that the copied value is a deep copy
        assert( &o3.get_value() != &o1.get_value() );

        assert( !o4.is_valid() );

        // copy o1 into o4
        o4 = o1;

        assert( o4.is_valid() );
        assert( o4.get_value() == o1.get_value() );

        // make sure that the copied value is a deep copy
        assert( &o4.get_value() != &o1.get_value() );

        // create a new, different optional
        Optional<std::vector<int>> o5 { std::vector<int>{ 4, 5, 6, 7 } };

        // overwrite existing values
        o3 = o5;

        assert( o3.is_valid() );
        assert( o3.get_value() == o5.get_value() );

        // make sure that the copied value is a deep copy
        assert( &o3.get_value() != &o5.get_value() );

        // clear out previous value with empty optional
        o4 = Optional<std::vector<int>>{ };

        assert( !o4.is_valid() );
    }
}